[Xorp-hackers] XORP, Click and its configuration...

Rafael Paoliello Guimaraes rafael.guimaraes@ac.upc.edu
Wed, 26 Jan 2005 13:39:41 +0100


This is a multi-part message in MIME format.
--------------060504090302010507020407
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Hello,

I was trying to use the click element KernelTun in my click 
configuration in order to allow packets generated by the same computer 
to be routed through click. In order to do this, I have changed the 
xorp_fea_click_config_generator script. This new script (which I am 
attaching to this email) works fine, it creates a new TUN interface 
(tun0) with 172.16.0.1 as its address. Finally, I have to create a 
default route to this new interface and delete every other route (so 
that all generated packets go through tun0) and configured linux not to 
do forwarding (in order to let this functionality to click).

By doing this, I am able to ping to every node and almost everything 
goes fine. The only problem is that, whenever I create a new route (I 
tried through static_routes), the TUN interfaces changes from tun0 to 
tun1. And if I create another route (or delete an existant one), it goes 
from tun0 to tun1. Why is this happening? Does XORP reinitiate Click 
after each route change? How can I avoid this?

Cheers,

-- 

===========================================
  Rafael Paoliello Guimaraes
  PhD Student - Computer Networking Group
  Department of Computer Architecture (DAC)
  Polytechnic University of Catalonia (UPC)
  Phone: +34-934017187   Fax: +34-934017055
  URL: http://people.ac.upc.es/rpaoliel
===========================================

--------------060504090302010507020407
Content-Type: text/plain;
 name="xorp_fea_click_config_generator"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="xorp_fea_click_config_generator"

#!/usr/bin/awk -f
#
# $XORP: xorp/fea/xorp_fea_click_config_generator,v 1.9 2004/12/18 02:03:21 pavlin Exp $
#

#
# A sample script to generate Click configuration from XORP configuration.
# The first argument is the XORP configuration file name.
#
# Currently, the script is called on-demand by the FEA whenever
# the network interface information changes.
#
# Requirements:
# 1. The Click lookup element name must be "_xorp_rt".
#    That element must support interface similar to element LinearIPLookup()
#    for adding and removing forwarding entries.
# 2. The first network interface/vif must be connected to _xorp_rt[0],
#    the second network interface/vif must be connected to _xorp_rt[1],
#    and so-on.
#
# Note that if both kernel-level and user-level Click are enabled
# (which is allowed), this script will print the kernel-level Click
# configuration.
#

BEGIN {
    # Various constants
    ipv4_addr_bitlen = 32;	# IPv4 address bitmask length
    ipv6_addr_bitlen = 128;	# IPv6 address bitmask length

    iftree_interfaces_max = 0;
    iftree_vifs_max = 0;
    is_click_enabled = 0;
    is_kernel_click = 0;
    is_user_click = 0;
    is_debug = 0;

    xorp_ip = "_xorp_ip";
    xorp_rt = "_xorp_rt";
    xorp_arpt = "_xorp_arpt";
    xorp_toh = "_xorp_toh";
    # TODO: XXX: fix ether_encap if necessary
    ether_encap = "EtherEncap(0x0800, 1:1:1:1:1:1, 2:2:2:2:2:2)";

    statement = "";
    in_comment = 0;
    is_syntax_error = 0;
    is_internal_error = 0;
    config_level = 0;
    ignore_level[0] = 1;
    is_level0_interfaces = 0;
    is_level0_fea = 0;
    is_level0_fea_level1_click = 0;
    is_level0_fea_level1_click_level2_kernel_click = 0;
    is_level0_fea_level1_click_level2_user_click = 0;
    is_ipv4_address = 0;
    is_ipv6_address = 0;
    max_xorp_rt_port = 0;
    iftree_interfaces_targetname = "fea";
    fea_targetname = "fea";

    # KernelTun Initializations: rafael
#    kerneltun_ip_addr = "192.168.254.1"
#    kerneltun_ip_prefix = "24"
#    kerneltun_port = 0;
}

function calculate_state()
{
    iftree_vifs_max = 0;
    xorp_rt_port = 0;

    #
    # Clear-up some temporary state
    # At the same time, compute the value of iftree_vifs_max
    #
    for (ii = 0; ii < iftree_interfaces_max; ii++) {
	ifaces_chosen[ii] = 0;
	for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) {
	    ifaces_vifs_chosen[ii,vi] = 0;
	    iftree_vifs_max++;
	}
    }

    #
    # Select the interfaces and vifs by using string comparison:
    # the "smaller" name first.
    #
    for (ci = 0; ci < iftree_interfaces_max; ci++) {
	best_ii = -1;
	for (ii = 0; ii < iftree_interfaces_max; ii++) {
	    if (ifaces_chosen[ii])
		continue;
	    if (best_ii == -1) {
		best_ii = ii;
		continue;
	    }
	    best_ifname = iftree_interfaces_ifname[best_ii];
	    if (iftree_interfaces_ifname[ii] < best_ifname) {
		best_ii = ii;
	    }
	}
	if (best_ii < 0) {
	    internal_error("Internal error: cannot sort the interface names\n");
	}
	ifaces_chosen[best_ii] = 1;

	for (cv = 0; cv < iftree_interfaces_vifs_max[best_ii]; cv++) {
	    best_vi = -1;
	    for (vi = 0; vi < iftree_interfaces_vifs_max[best_ii]; vi++) {
		if (ifaces_vifs_chosen[best_ii,vi])
		    continue;
		if (best_vi == -1) {
		    best_vi = vi;
		    continue;
		}
		best_vifname = iftree_interfaces_vifs_vifname[best_ii,best_vi];
		if (iftree_interfaces_vifs_vifname[best_ii,vi] < best_vifname) {
		    best_vi = vi;
		}
	    }
	    if (best_vi < 0) {
		internal_error("Internal error: cannot sort the vif names\n");
	    }
	    ifaces_vifs_chosen[best_ii,best_vi] = 1;

	    iftree_interfaces_vifs_port[best_ii,best_vi] = xorp_rt_port++;
	}
        # KernelTun: rafael
#        kerneltun_port = xorp_rt_port++;
    }

    max_xorp_rt_port = xorp_rt_port;
}

function check_state()
{
    for (ii = 0; ii < iftree_interfaces_max; ii++) {
	ifname = iftree_interfaces_ifname[ii];
	if (iftree_interfaces_has_mac[ii] != 1) {
	    message = "Missing MAC address configuration for interface " ifname;
	    syntax_error(message);
	}
	if (iftree_interfaces_has_mtu[ii] != 1) {
	    message = "Missing MTU configuration for interface " ifname;
	    syntax_error(message);
	}
    }
}


function generate_click_config_header()
{
    printf("//\n");
    printf("// Generated by XORP FEA\n");
    printf("//\n");
}


function generate_kerneltun()
{
    printf("\nelementclass FixChecksums {\n");
    printf("    input -> SetIPChecksum\n");
    printf("          -> ipc :: IPClassifier(tcp, udp, -)\n");
    printf("          -> SetTCPChecksum\n");
    printf("          -> output;\n");
    printf("    ipc[1] -> SetUDPChecksum -> output;\n");
    printf("    ipc[2] -> output\n");
    printf("}\n\n");

    printf("tun :: KernelTun(172.16.0.1/24) -> Paint(0) -> _xorp_rt;\n\n");
}

function generate_click_shared_ip_input()
{
    check_ip_header = "CheckIPHeader(INTERFACES";
    local_ip_routes = "";
    is_first_address = 1;

    for (ii = 0; ii < iftree_interfaces_max; ii++) {
	for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) {
	    for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) {
		addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4];
		prefix4 = iftree_interfaces_vifs_addresses4_prefix[ii,vi,ai4];
		check_ip_header = check_ip_header " " addr4 "/" prefix4;
		route = addr4 "/" ipv4_addr_bitlen " " max_xorp_rt_port;
		if (is_first_address) {
		    local_ip_routes = route;
		    is_first_address = 0;
		} else {
		    local_ip_routes = local_ip_routes ", " route;
		}
	    }
	}
    }

    check_ip_header = check_ip_header ")";

    printf("\n");
    printf("// Shared IP input path and routing table\n");
    printf("%s :: Strip(14)\n", xorp_ip);
    printf("    -> %s\n", check_ip_header);
    printf("    -> %s :: LinearIPLookup(%s);\n", xorp_rt, local_ip_routes);
}

function generate_click_arp_responses()
{
    printf("\n");
    printf("// ARP responses are copied to each ARPQuerier and the host.\n");
    printf("%s :: Tee(%u);\n", xorp_arpt, iftree_vifs_max + 1);
}

function generate_click_input_output_paths()
{
    port = 0;

    for (ii = 0; ii < iftree_interfaces_max; ii++) {
	for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) {
	    xorp_c = "_xorp_c" port;
	    xorp_out = "_xorp_out" port;
	    xorp_to_device = "_xorp_to_device" port;
	    xorp_ar = "_xorp_ar" port;
	    xorp_arpq = "_xorp_arpq" port;

	    if (! iftree_interfaces_vifs_enabled[ii,vi]) {
		from_device = "NullDevice";
	    } else {
		#
		# TODO: XXX: we should find a way to configure polling
		# devices. Such devices should use "PollDevice" instead
		# of "FromDevice".
		#
		from_device = "FromDevice(" iftree_interfaces_vifs_vifname[ii,vi] ")";
	    }

	    if (! iftree_interfaces_vifs_enabled[ii,vi]) {
		to_device = "Discard";
	    } else {
		to_device = "ToDevice(" iftree_interfaces_vifs_vifname[ii,vi] ")";
	    }

	    arp_responder = "ARPResponder(";
	    mac = iftree_interfaces_mac[ii];
	    is_begin = 1;
	    for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) {
		addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4];
		if (is_begin)
		    arp_responder = arp_responder addr4;
		else
		    arp_responder = arp_responder " " addr4;
		is_begin = 0;
	    }
	    arp_responder = arp_responder " " mac ")";

	    first_addr4 = "0.0.0.0";
	    if (iftree_interfaces_vifs_addresses4_max[ii,vi] > 0) {
		first_addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,0];
	    }
	    arp_querier = "ARPQuerier(" first_addr4 ", " mac ")";

	    paint = "Paint(" port + 1 ")";
	    print_non_ip = "Print(\"" iftree_interfaces_vifs_vifname[ii,vi] " non-IP\")";

	    printf("\n");
	    printf("// Input and output paths for %s\n",
		   iftree_interfaces_vifs_vifname[ii,vi]);
	    printf("%s :: Classifier(12/0806 20/0001, 12/0806 20/0002, 12/0800, -);\n",
		   xorp_c);
	    printf("%s -> %s;\n",
		   from_device,
		   xorp_c);
	    printf("%s :: Queue(200) -> %s :: %s;\n",
		   xorp_out,
		   xorp_to_device,
		   to_device);
	    printf("%s[0] -> %s :: %s -> %s;\n",
		   xorp_c,
		   xorp_ar,
		   arp_responder,
		   xorp_out);
	    printf("%s :: %s -> %s;\n",
		   xorp_arpq,
		   arp_querier,
		   xorp_out);
	    printf("%s[1] -> %s;\n",
		   xorp_c,
		   xorp_arpt);
	    printf("%s[%u] -> [1]%s;\n",
		   xorp_arpt,
		   port,
		   xorp_arpq);
	    printf("%s[2] -> %s -> %s;\n",
		   xorp_c,
		   paint,
		   xorp_ip);
	    printf("%s[3] -> %s -> Discard;\n",
		   xorp_c,
		   print_non_ip);
	    port++;
	}
    }
    # KernelTun ARPResponder configuration: rafael
}

function generate_click_send_packets_to_host()
{
    printf("\n");
    printf("// Local delivery\n");
    # TODO: XXX: Fix the Local delivery for *BSD and/or user-level Click
    if (is_kernel_click) {
	#
	# XXX: Note that if both kernel-level and user-level Click are enabled
	# (which is allowed), this script will print the kernel-level Click
	# configuration.
	#
	printf("%s[%u] -> ToHost;\n",
	       xorp_arpt,
	       iftree_vifs_max);
        # XXX: last port in xorp_rt is reserved for local delivery
	printf("%s[%u] -> %s -> ToHost;\n",
	       xorp_rt,
	       max_xorp_rt_port,
	       ether_encap);
    } else {
	printf("%s[%u] -> Discard;\n",
	       xorp_arpt,
	       iftree_vifs_max);
        # XXX: last port in xorp_rt is reserved for local delivery
	printf("%s[%u] -> Discard;\n",
	       xorp_rt,
	       max_xorp_rt_port);
    }
}

function generate_click_forwarding_paths()
{
    #
    # Forwarding paths for each interface
    #
    port = 0;
    for (ii = 0; ii < iftree_interfaces_max; ii++) {
	for (vi = 0; vi < iftree_interfaces_vifs_max[ii]; vi++) {
	    xorp_cp = "_xorp_cp" port;
	    paint_tee = "PaintTee(" port + 1 ")";
	    xorp_gio = "_xorp_gio" port;
	    xorp_dt = "_xorp_dt" port;
	    xorp_fr = "_xorp_fr" port;
	    xorp_arpq = "_xorp_arpq" port;
            xorp_sw = "_xorp_sw" port;

	    ipgw_options = "IPGWOptions(";
	    is_begin = 1;
	    for (ai4 = 0; ai4 < iftree_interfaces_vifs_addresses4_max[ii,vi]; ai4++) {
		addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,ai4];
		if (is_begin)
		    ipgw_options = ipgw_options addr4;
		else
		    ipgw_options = ipgw_options ", " addr4;
		is_begin = 0;
	    }
	    ipgw_options = ipgw_options ")";

	    first_addr4 = "0.0.0.0";
	    if (iftree_interfaces_vifs_addresses4_max[ii,vi] > 0) {
		first_addr4 = iftree_interfaces_vifs_addresses4_addr[ii,vi,0];
	    }
	    fix_ip_src = "FixIPSrc(" first_addr4 ")";
	    mtu = iftree_interfaces_mtu[ii];
	    ip_fragmenter = "IPFragmenter(" mtu ")";

	    printf("\n");
	    printf("// Forwarding path for %s\n",
		   iftree_interfaces_vifs_vifname[ii,vi]);
	    xorp_rt_port = iftree_interfaces_vifs_port[ii,vi];
	    printf("%s[%u] -> DropBroadcasts -> %s :: PaintSwitch();\n",
		   xorp_rt,
		   xorp_rt_port,
		   xorp_sw);
	    printf("%s :: %s\n",
		   xorp_gio,
		   ipgw_options);
	    printf("    -> %s\n",
		   fix_ip_src);
	    printf("    -> %s :: DecIPTTL\n",
		   xorp_dt);
	    printf("    -> %s :: %s\n",
		   xorp_fr,
		   ip_fragmenter);
	    printf("    -> [0]%s;\n\n",
		   xorp_arpq);
	    printf("%s[0] -> StoreIPAddress(%s,12) -> FixChecksums -> %s;\n",
		   xorp_sw,
		   first_addr4,
		   xorp_fr);

	    for (jj = 0; jj < iftree_interfaces_max; jj++) {
		for (vj = 0; vj < iftree_interfaces_vifs_max[jj]; vj++) {
                    xorp_rt_port2 = iftree_interfaces_vifs_port[jj,vj];
		    if (jj == ii && vj == vi) {
			printf("%s[%u] -> %s :: Tee(2) -> ICMPError(%s, redirect, host) -> %s;\n",
			       xorp_sw,
			       xorp_rt_port2+1,
			       xorp_cp,
			       first_addr4,
			       xorp_rt);
		    } else {
			printf("%s[%u] -> %s;\n",
			       xorp_sw,
			       xorp_rt_port2+1,
			       xorp_gio);
		    }
		}
	    }
	    printf("%s[1] -> %s\n",
		   xorp_cp,
		   xorp_gio);
	    printf("%s[1] -> ICMPError(%s, timeexceeded) -> %s;\n",
		   xorp_dt,
		   first_addr4,
		   xorp_rt);
	    printf("%s[1] -> ICMPError(%s, unreachable, needfrag) -> %s;\n",
		   xorp_fr,
		   first_addr4,
		   xorp_rt);
	    printf("%s[1] -> ICMPError(%s, parameterproblem) -> %s;\n",
		   xorp_gio,
		   first_addr4,
		   xorp_rt);
	    port++;
	}
    }
}

function is_end_of_statement(token)
{
    # Get the last character
    l = length(token);
    s = substr(token, l, 1);
    if (s == ";")
	return 1;
    else
	return 0;
}

function is_end_of_string(token)
{
    # Get the last character
    l = length(token);
    s = substr(token, l, 1);
    if (s == "\"")
	return 1;
    else
	return 0;
}

function is_begin_of_comment(token)
{
    # Get the first two characters
    l = length(token);
    if (l < 2)
	return 0;
    s = substr(token, 1, 2);
    if (s == "/*")
	return 1;
    else
	return 0;
}

function is_end_of_comment(token)
{
    # Get the last two characters
    l = length(token);
    if (l < 2)
	return 0;
    s = substr(token, l-1, 2);
    if (s == "*/")
	return 1;
    else
	return 0;
}

function process_statement(statement)
{
    if (is_debug)
	printf("Statement: %s\n", statement);

    tokens_n = split(statement, tokens);
    for (i = 1; i <= tokens_n; i++) {
	# printf("Token: %s\n", tokens[i]);
    }
    # TODO: need to deal with strings

    for (i = 1; i <= tokens_n; i++) {
	if (tokens[i] == "{") {
	    config_level++;
	    continue;
	}
	if (tokens[i] == "}") {
	    config_level--;
	    if (config_level < 0)
		syntax_error("Too many }");
	    continue;
	}

	if (config_level == 0) {
	    ignore_level[0] = 1;
	    is_level0_interfaces = 0;
	    is_level0_fea = 0;
	    is_level0_fea_level1_click = 0;
	    is_level0_fea_level1_click_level2_kernel_click = 0;
	    is_level0_fea_level1_click_level2_user_click = 0;
	    if (tokens[i] == "interfaces") {
		ignore_level[0] = 0;
		is_level0_interfaces = 1;
	    }
	    if (tokens[i] == "fea") {
		ignore_level[0] = 0;
		is_level0_fea = 1;
		is_level0_fea_level1_click = 0;
		is_level0_fea_level1_click_level2_kernel_click = 0;
		is_level0_fea_level1_click_level2_user_click = 0;
	    }
	}
	if (ignore_level[0])
	    continue;

	#
	# Configuration section: "interfaces {}"
	#
	if (is_level0_interfaces && (config_level == 1)) {
	    if (tokens[i] == "targetname:") {
		if (i == tokens_n)
		    syntax_error("Missing target name");
		i++;
		v = tokens[i];
		iftree_interfaces_targetname = v;
		continue;
	    }

	    if (tokens[i] == "interface") {
		if (i == tokens_n)
		    syntax_error("Missing interface name");
		i++;
		# TODO: check that the interface name is valid
		v = tokens[i];
		iftree_interfaces_max++;
		iftree_interfaces_ifname[iftree_interfaces_max - 1] = v;
		# XXX: by default each interface is enabled
		iftree_interfaces_enabled[iftree_interfaces_max - 1] = 1;
		continue;
	    }
	    syntax_error("Invalid keyword");
	}


	if (is_level0_interfaces && (config_level == 2)) {
	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		if (v == "true")
		    iftree_interfaces_enabled[iftree_interfaces_max - 1] = 1;
		else
		    iftree_interfaces_enabled[iftree_interfaces_max - 1] = 0;
		continue;
	    }

	    if (tokens[i] == "discard:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		iftree_interfaces_discard[iftree_interfaces_max - 1] = v;
		continue;
	    }

	    if (tokens[i] == "description:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		while (! is_end_of_string(v)) {
		    if (i == tokens_n)
			syntax_error("Missing end-of-string");
		    i++;
		    v = v " " tokens[i];
		}
		iftree_interfaces_description[iftree_interfaces_max - 1] = v;
		continue;
	    }

	    if (tokens[i] == "mac:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		iftree_interfaces_mac[iftree_interfaces_max - 1] = v;
		iftree_interfaces_has_mac[iftree_interfaces_max - 1] = 1;
		continue;
	    }

	    if (tokens[i] == "mtu:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		iftree_interfaces_mtu[iftree_interfaces_max - 1] = v;
		iftree_interfaces_has_mtu[iftree_interfaces_max - 1] = 1;
		continue;
	    }

	    if (tokens[i] == "vif") {
		if (i == tokens_n)
		    syntax_error("Missing vif name");
		i++;
		# TODO: check that the vif name is valid
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		iftree_interfaces_vifs_max[p]++;
		q = iftree_interfaces_vifs_max[p] - 1;
		iftree_interfaces_vifs_vifname[p,q] = v;
		# XXX: by default each vif is enabled
		iftree_interfaces_vifs_enabled[p,q] = 1;
		continue;
	    }
	    syntax_error("Invalid keyword");
	}

	if (is_level0_interfaces && (config_level == 3)) {
	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (v == "true")
		    iftree_interfaces_vifs_enabled[p,q] = 1;
		else
		    iftree_interfaces_vifs_enabled[p,q] = 0;
		continue;
	    }

	    if (tokens[i] == "address") {
		if (i == tokens_n)
		    syntax_error("Missing address value");
		i++;
		# TODO: check that the address value is valid
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		is_ipv4_address = 0;
		is_ipv6_address = 0;
		if (split(v, tmp_array, ".") == 4)
		    is_ipv4_address = 1;
		if (split(v, tmp_array, ":") > 1)
		    is_ipv6_address = 1;
		if (!( is_ipv4_address || is_ipv6_address))
		    syntax_error("Invalid address value");
		if (is_ipv4_address) {
		    iftree_interfaces_vifs_addresses4_max[p,q]++;
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_addr[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    iftree_interfaces_vifs_addresses6_max[p,q]++;
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_addr[p,q,r] = v;
		}
		continue;
	    }
	    syntax_error("Invalid keyword");
	}

	if (is_level0_interfaces && (config_level == 4)) {
	    if (tokens[i] == "prefix-length:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_prefix[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_prefix[p,q,r] = v;
		}
		continue;
	    }

	    if (tokens[i] == "broadcast:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_destination[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    syntax_error("Invalid keyword");
		}
		continue;
	    }

	    if (tokens[i] == "destination:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_destination[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_destination[p,q,r] = v;
		}
		continue;
	    }

	    if (tokens[i] == "multicast-capable:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_multicast[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_multicast[p,q,r] = v;
		}
		continue;
	    }

	    if (tokens[i] == "point-to-point:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_p2p[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_p2p[p,q,r] = v;
		}
		continue;
	    }

	    if (tokens[i] == "loopback:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses4_loopback[p,q,r] = v;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    iftree_interfaces_vifs_addresses6_loopback[p,q,r] = v;
		}
		continue;
	    }

	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		p = iftree_interfaces_max - 1;
		q = iftree_interfaces_vifs_max[p] - 1;
		if (is_ipv4_address) {
		    r = iftree_interfaces_vifs_addresses4_max[p,q] - 1;
		    if (v == "true")
			iftree_interfaces_vifs_addresses4_enabled[p,q,r] = 1;
		    else
			iftree_interfaces_vifs_addresses4_enabled[p,q,r] = 0;
		}
		if (is_ipv6_address) {
		    r = iftree_interfaces_vifs_addresses6_max[p,q] - 1;
		    if (v == "true")
			iftree_interfaces_vifs_addresses6_enabled[p,q,r] = 1;
		    else
			iftree_interfaces_vifs_addresses6_enabled[p,q,r] = 0;
		}
		continue;
	    }

	    syntax_error("Invalid keyword");
	}

	#
	# Configuration section: "fea {}"
	#
	if (is_level0_fea && (config_level == 1)) {
	    if (tokens[i] == "targetname:") {
		if (i == tokens_n)
		    syntax_error("Missing target name");
		i++;
		v = tokens[i];
		fea_targetname = v;
		continue;
	    }

	    if (tokens[i] == "click") {
		is_level0_fea_level1_click = 1;
		is_level0_fea_level1_click_level2_kernel_click = 0;
		is_level0_fea_level1_click_level2_user_click = 0;
		continue;
	    }

	    # XXX: ignore the rest of the configuration
	    continue;
	}

	if (is_level0_fea && is_level0_fea_level1_click &&
	    (config_level == 2)) {

	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		if (v == "true")
		    is_click_enabled = 1;
		else
		    is_click_enabled = 0;
		continue;
	    }

	    if (tokens[i] == "kernel-click") {
		is_level0_fea_level1_click_level2_kernel_click = 1;
		is_level0_fea_level1_click_level2_user_click = 0;
		continue;
	    }

	    if (tokens[i] == "user-click") {
		is_level0_fea_level1_click_level2_kernel_click = 0;
		is_level0_fea_level1_click_level2_user_click = 1;
		continue;
	    }

	    # XXX: ignore the rest of the configuration
	    continue;
	}

	if (is_level0_fea && is_level0_fea_level1_click &&
	    is_level0_fea_level1_click_level2_kernel_click &&
	    (config_level == 3)) {
	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		if (v == "true") {
		    is_kernel_click = 1;
		} else {
		    is_kernel_click = 0;
		}
		continue;
	    }

	    # XXX: ignore the rest of the configuration
	    continue;
	}

	if (is_level0_fea && is_level0_fea_level1_click &&
	    is_level0_fea_level1_click_level2_user_click &&
	    (config_level == 3)) {
	    if (tokens[i] == "enabled:") {
		if (i == tokens_n)
		    syntax_error("Missing value");
		i++;
		v = tokens[i];
		if (v == "true") {
		    is_user_click = 1;
		} else {
		    is_user_click = 0;
		}
		continue;
	    }

	    # XXX: ignore the rest of the configuration
	    continue;
	}
    }
}

function syntax_error(message)
{
    is_syntax_error = 1;
    printf("Syntax error (file %s line %d): %s\n", FILENAME, FNR, message);
    exit(1);
}

function internal_error(message)
{
    is_internal_error = 1;
    printf("Internal error (file %s): %s\n", FILENAME, message);
    exit(1);
}

{
    for (i = 1; i <= NF; i++) {
	token = $i;

	# Filter-out the comments
	if (is_begin_of_comment(token)) {
	    if (in_comment)
		syntax_error("Nested comments");
	    in_comment = 1;
	}
	if (in_comment) {
	    if (is_end_of_comment(token))
		in_comment = 0;
	    continue;
	}
	if (is_end_of_comment(token))
	    syntax_error("Missing begin-of-comment");

	statement = statement " " token;
	if (is_end_of_statement(token)) {
	    process_statement(statement);
	    statement = "";
	}
    }
    process_statement(statement);
    statement = "";
}

END {
    #
    # Initialize internal state
    #
    calculate_state();
    check_state();

    #
    # Check for syntax and internal errors
    #
    if (is_syntax_error || is_internal_error)
	exit(1);

    #
    # Check if Click is enabled and whether it is user-level or kernel Click
    #
    if (! is_click_enabled)
	exit(0);		# XXX: don't print any Click configuration
    if ((! is_user_click) && (! is_kernel_click))
	exit(0);		# XXX: don't print any Click configuration

    generate_click_config_header();
    generate_click_shared_ip_input();
    generate_kerneltun();
    generate_click_arp_responses();
    generate_click_input_output_paths();
    generate_click_send_packets_to_host();
    generate_click_forwarding_paths();

    #
    # Check again for syntax and internal errors
    #
    if (is_syntax_error || is_internal_error)
	exit(1);

    exit(0);
}

# Local Variables:
# mode: AWK
# sh-indentation: 4
# End:

--------------060504090302010507020407--