def test_all_routes(): add_missing_methods_to_thrift() route_table = mkrt(ADDRESS_FAMILY_IPV4) # Use all_routes generator to walk empty table generator = route_table.all_routes() with pytest.raises(Exception): next(generator) # Add some routes to the table (purposely in the wrong order) route_table.put_route(mkr("2.2.2.0/24", N)) route_table.put_route(mkr("1.1.1.0/24", N)) route_table.put_route(mkr("0.0.0.0/0", S)) route_table.put_route(mkr("1.1.1.0/24", S)) route_table.put_route(mkr("2.2.0.0/16", S)) # Use the generator to visit all routes generator = route_table.all_routes() rte = next(generator) assert rte.prefix == mkp("0.0.0.0/0") assert rte.owner == S rte = next(generator) assert rte.prefix == mkp("1.1.1.0/24") assert rte.owner == S rte = next(generator) assert rte.prefix == mkp("1.1.1.0/24") assert rte.owner == N rte = next(generator) assert rte.prefix == mkp("2.2.0.0/16") assert rte.owner == S rte = next(generator) assert rte.prefix == mkp("2.2.2.0/24") assert rte.owner == N with pytest.raises(Exception): next(generator)
def test_ipv6_table_get_route(): add_missing_methods_to_thrift() route_table = mkrt(ADDRESS_FAMILY_IPV6) # Try to get a route that is not present in the table (empty table) assert route_table.get_route(mkp("1111:1111:1111:1111:0000:0000:0000:0000/64"), S) is None # Try to get a route that is not present in the table (prefix is not present) route_table.put_route(mkr("2222:2222:2222:2222:2222:2222:2222:2222/128", S)) assert route_table.get_route(mkp("3333:3333:3333:3333:3333:0000:0000:0000/80"), S) is None # Try to get a route that is not present in the table (prefix length is wrong) assert route_table.get_route(mkp("2222:2222:2222:2222:2222:2222:2222:0000/112"), S) is None # Try to get a route that is not present in the table (owner is not present) assert route_table.get_route(mkp("2222:2222:2222:2222:2222:2222:2222:2222/128"), N) is None # Get a route that is present in the table (only route for prefix) rte = route_table.get_route(mkp("2222:2222:2222:2222:2222:2222:2222:2222/128"), S) assert rte is not None assert rte.prefix == mkp("2222:2222:2222:2222:2222:2222:2222:2222/128") assert rte.owner == S # Add other routes to the table route_table.put_route(mkr("1111:1111::/32", N)) route_table.put_route(mkr("2222:2222:2222:2222:2222:2222:2222:2222/128", N)) route_table.put_route(mkr("2222:2222:2222::/48", N)) route_table.put_route(mkr("3333::/16", S)) # Get a route that is present in the table (multiple routes for prefix, get most preferred) rte = route_table.get_route(mkp("2222:2222:2222:2222:2222:2222:2222:2222/128"), S) assert rte is not None assert rte.prefix == mkp("2222:2222:2222:2222:2222:2222:2222:2222/128") assert rte.owner == S # Get a route that is present in the table (multiple routes for prefix, get least preferred) rte = route_table.get_route(mkp("2222:2222:2222:2222:2222:2222:2222:2222/128"), N) assert rte is not None assert rte.prefix == mkp("2222:2222:2222:2222:2222:2222:2222:2222/128") assert rte.owner == N
def test_fix_tire_packet(): packet_common.add_missing_methods_to_thrift() tire_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=encoding.ttypes.TIREPacket( headers=set([ max_tie_header(0), max_tie_header(1), max_tie_header(2) ]) ), tie=None ) ) encoded_packet = packet_common.encode_protocol_packet(tire_protocol_packet) decoded_tire_protocol_packet = packet_common.decode_protocol_packet(encoded_packet) assert tire_protocol_packet == decoded_tire_protocol_packet
def test_fix_external_prefixes_tie_packet(): packet_common.add_missing_methods_to_thrift() tie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=None, tie=encoding.ttypes.TIEPacket( header=max_tie_header(), element=encoding.ttypes.TIEElement( node=None, prefixes=None, positive_disaggregation_prefixes=None, negative_disaggregation_prefixes=None, external_prefixes=max_prefix_tie_element(), keyvalues=None ) ) ) ) packet_info = packet_common.encode_protocol_packet(tie_protocol_packet, None) packet_info.update_env_header(0) packet_info.update_outer_sec_env_header(None, 111, 222, 10) message = b''.join(packet_info.message_parts()) decoded_packet_info = packet_common.decode_message(None, None, message, None, None, None, None) assert not decoded_packet_info.error assert packet_info.protocol_packet == decoded_packet_info.protocol_packet
def test_fix_key_value_tie_packet(): packet_common.add_missing_methods_to_thrift() tie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=None, tie=encoding.ttypes.TIEPacket( header=max_tie_header(), element=encoding.ttypes.TIEElement( node=None, prefixes=None, positive_disaggregation_prefixes=None, negative_disaggregation_prefixes=None, external_prefixes=None, keyvalues=encoding.ttypes.KeyValueTIEElement( keyvalues={ "one": "een", "two": "twee" } ) ) ) ) ) encoded_packet = packet_common.encode_protocol_packet(tie_protocol_packet) decoded_tie_protocol_packet = packet_common.decode_protocol_packet(encoded_packet) assert tie_protocol_packet == decoded_tie_protocol_packet
def test_fix_lie_packet(): packet_common.add_missing_methods_to_thrift() lie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16), content=encoding.ttypes.PacketContent(lie=encoding.ttypes.LIEPacket( name="name", local_id=packet_common.MAX_U32, flood_port=packet_common.MAX_U16, link_mtu_size=packet_common.MAX_U32, link_bandwidth=packet_common.MAX_U32, neighbor=encoding.ttypes.Neighbor(originator=packet_common.MAX_U64, remote_id=packet_common.MAX_U32), pod=packet_common.MAX_U32, nonce=packet_common.MAX_U16, last_neighbor_nonce=packet_common.MAX_U16, node_capabilities=encoding.ttypes.NodeCapabilities( flood_reduction=True, hierarchy_indications=common.ttypes.HierarchyIndications. leaf_only), link_capabilities=encoding.ttypes.LinkCapabilities(bfd=False, ), holdtime=packet_common.MAX_U16, not_a_ztp_offer=True, you_are_flood_repeater=True, label=packet_common.MAX_U32), tide=None, tire=None, tie=None)) encoded_packet = packet_common.encode_protocol_packet(lie_protocol_packet) decoded_lie_protocol_packet = packet_common.decode_protocol_packet( encoded_packet) assert lie_protocol_packet == decoded_lie_protocol_packet
def test_ipv4_table_put_route(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) nh1 = mknh("if1", "1.1.1.1") nh2 = mknh("if2", "2.2.2.2") assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Add a route # No next_hop route_table.put_route(mkr("1.1.1.0/24", N)) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Add two routes with same prefix but with different owners (most preferred added first) # One next_hops each route_table.put_route(mkr("2.2.2.2/32", S, [nh1])) route_table.put_route(mkr("2.2.2.2/32", N, [nh2])) assert route_table.nr_destinations() == 2 assert route_table.nr_routes() == 3 # Add two routes with same prefix but with different owners (most preferred added last) # Two next_hops each route_table.put_route(mkr("3.3.3.0/24", N, [nh1, nh2])) route_table.put_route(mkr("3.3.3.0/24", S, [nh1, nh2])) assert route_table.nr_destinations() == 3 assert route_table.nr_routes() == 5 # Replace route which is already in the table (only one route to prefix) route_table.put_route(mkr("4.4.4.0/30", N)) route_table.put_route(mkr("4.4.4.0/30", N)) assert route_table.nr_destinations() == 4 assert route_table.nr_routes() == 6 # Replace route which is already in the table (multiple routes to prefix) route_table.put_route(mkr("0.0.0.0/0", S)) route_table.put_route(mkr("0.0.0.0/0", N)) route_table.put_route(mkr("0.0.0.0/0", N)) assert route_table.nr_destinations() == 5 assert route_table.nr_routes() == 8
def test_process_tide(): # TODO: Also have other TIEs than prefix TIEs packet_common.add_missing_methods_to_thrift() db_tie_info_list = [ # pylint:disable=bad-whitespace # Direction Origin Type TieNr SeqNr Lifetime Disposition (SOUTH, 8, PREFIX, 1, 1, 100), # In gap before TIDE-1; start (SOUTH, 10, PREFIX, 1, 2, 100), # Not in TIDE-1 (start gap); start (SOUTH, 10, PREFIX, 2, 5, 100), # Not in TIDE-1 (start gap); start (SOUTH, 10, PREFIX, 10, 10, 100), # Same version as in TIDE; stop (SOUTH, 10, PREFIX, 12, 5, 100), # Not in TIDE-1 (middle gap); start (SOUTH, 10, PREFIX, 13, 3, 100), # Older version than in TIDE-1; request (NORTH, 3, PREFIX, 15, 7, 100), # Newer version than in TIDE-1; start (NORTH, 4, PREFIX, 1, 1, 100), # Not in TIDE-1 (end gap); start (NORTH, 10, PREFIX, 7, 6, 100), # In TIDE-1...TIDE-2 gap; start (NORTH, 21, PREFIX, 15, 3, 100), # Older version than in TIDE-2; request (NORTH, 110, PREFIX, 40, 1, 100), # In TIDE-2...TIDE-3 gap; start (NORTH, 210, PREFIX, 6, 6, 100) ] # Not in TIDE-3 (empty); start test_node = make_test_node(db_tie_info_list) check_process_tide_1(test_node) check_process_tide_2(test_node) check_process_tide_3(test_node) # Test wrap-around. Finished sending all TIDEs, now send first TIDE again. The key test is # whether the TIE in the TIE-DB in the gap before TIDE-1 is put on the send queue again. check_process_tide_1(test_node)
def test_ipv4_table_get_route(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) # Try to get a route that is not present in the table (empty table) assert route_table.get_route(mkp("1.1.1.0/24"), S) is None # Try to get a route that is not present in the table (prefix is not present) route_table.put_route(mkr("2.2.2.0/32", S)) assert route_table.get_route(mkp("3.3.3.0/24"), S) is None # Try to get a route that is not present in the table (prefix length is wrong) assert route_table.get_route(mkp("2.2.2.0/24"), S) is None # Try to get a route that is not present in the table (owner is not present) assert route_table.get_route(mkp("2.2.2.0/32"), N) is None # Get a route that is present in the table (only route for prefix) rte = route_table.get_route(mkp("2.2.2.0/32"), S) assert rte is not None assert rte.prefix == mkp("2.2.2.0/32") assert rte.owner == S # Add other routes to the table route_table.put_route(mkr("1.1.0.0/16", N)) route_table.put_route(mkr("2.2.2.0/32", N)) route_table.put_route(mkr("2.2.2.0/24", N)) route_table.put_route(mkr("3.0.0.0/8", S)) # Get a route that is present in the table (multiple routes for prefix, get most preferred) rte = route_table.get_route(mkp("2.2.2.0/32"), S) assert rte is not None assert rte.prefix == mkp("2.2.2.0/32") assert rte.owner == S # Get a route that is present in the table (multiple routes for prefix, get least preferred) rte = route_table.get_route(mkp("2.2.2.0/32"), N) assert rte is not None assert rte.prefix == mkp("2.2.2.0/32") assert rte.owner == N
def test_cli_table(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) nh1 = mknh("if1", "1.1.1.1") nh2 = mknh("if2", "2.2.2.2") nh3 = mknh("if3", None) route_table.put_route(mkr("2.2.2.0/24", N)) route_table.put_route(mkr("1.1.1.0/24", S, [nh1])) route_table.put_route(mkr("0.0.0.0/0", S, [nh1, nh2])) route_table.put_route(mkr("2.2.0.0/16", S, [nh3])) route_table.put_route(mkr("1.1.1.0/24", N)) tab_str = route_table.cli_table().to_string() assert (tab_str == "+------------+-----------+-------------+\n" "| Prefix | Owner | Next-hops |\n" "+------------+-----------+-------------+\n" "| 0.0.0.0/0 | South SPF | if1 1.1.1.1 |\n" "| | | if2 2.2.2.2 |\n" "+------------+-----------+-------------+\n" "| 1.1.1.0/24 | South SPF | if1 1.1.1.1 |\n" "+------------+-----------+-------------+\n" "| 1.1.1.0/24 | North SPF | |\n" "+------------+-----------+-------------+\n" "| 2.2.0.0/16 | South SPF | if3 |\n" "+------------+-----------+-------------+\n" "| 2.2.2.0/24 | North SPF | |\n" "+------------+-----------+-------------+\n")
def test_fix_tire_packet(): packet_common.add_missing_methods_to_thrift() tire_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=encoding.ttypes.TIREPacket( headers=set([ max_tie_header_lifetime(0), max_tie_header_lifetime(1), max_tie_header_lifetime(2) ]) ), tie=None ) ) packet_info = packet_common.encode_protocol_packet(tire_protocol_packet, None) packet_info.update_env_header(0) packet_info.update_outer_sec_env_header(None, 111, 222) message = b''.join(packet_info.message_parts()) decoded_packet_info = packet_common.decode_message(None, None, message, None, None, None, None) assert not decoded_packet_info.error assert packet_info.protocol_packet == decoded_packet_info.protocol_packet
def test_all_prefix_routes(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) # Use all_routes generator to walk empty table generator = route_table.all_prefix_routes(mkp("1.2.3.4/32")) with pytest.raises(Exception): next(generator) # Add some routes to the table (purposely in the wrong order) route_table.put_route(mkr("2.2.2.0/24", N)) route_table.put_route(mkr("1.1.1.0/24", N)) route_table.put_route(mkr("0.0.0.0/0", S)) route_table.put_route(mkr("1.1.1.0/24", S)) route_table.put_route(mkr("2.2.0.0/16", S)) # Use the generator to visit all routes for prefix 3.3.0.0/16 (there are none) generator = route_table.all_prefix_routes(mkp("3.3.0.0/16")) with pytest.raises(Exception): next(generator) # Use the generator to visit all routes for prefix 2.2.0.0/16 (there is one) generator = route_table.all_prefix_routes(mkp("2.2.0.0/16")) rte = next(generator) assert rte.prefix == mkp("2.2.0.0/16") assert rte.owner == S with pytest.raises(Exception): next(generator) # Use the generator to visit all routes for prefix 1.1.1.0/24 (there are two) generator = route_table.all_prefix_routes(mkp("1.1.1.0/24")) rte = next(generator) assert rte.prefix == mkp("1.1.1.0/24") assert rte.owner == S rte = next(generator) assert rte.prefix == mkp("1.1.1.0/24") assert rte.owner == N with pytest.raises(Exception): next(generator)
def test_asserts(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) # Passing the wrong prefix type to assert_prefix_address_family asserts with pytest.raises(Exception): packet_common.assert_prefix_address_family( "1.2.3.0/24", constants.ADDRESS_FAMILY_IPV4) with pytest.raises(Exception): packet_common.assert_prefix_address_family( mkp("1.2.3.0/24"), constants.ADDRESS_FAMILY_IPV6) with pytest.raises(Exception): packet_common.assert_prefix_address_family( mkp("::1.2.3.0/24"), constants.ADDRESS_FAMILY_IPV4) with pytest.raises(Exception): packet_common.assert_prefix_address_family(mkp("1.2.3.0/24"), 999) # Passing the wrong prefix type to the Route constructor asserts with pytest.raises(Exception): _rte = rib_route.RibRoute("1.2.3.0/24", N, []) # Passing the wrong prefix type to get_route asserts with pytest.raises(Exception): _rte = route_table.get_route("1.2.3.0/24", N) # Passing the wrong prefix type to del_route asserts with pytest.raises(Exception): _deleted = route_table.del_route("1.2.3.0/24", N) # The address family of the route must match the address family of the table with pytest.raises(Exception): route_table.put_route( mkr("1111:1111:1111:1111:0000:0000:0000:0000/64", N))
def test_ipv6_table_put_route(): add_missing_methods_to_thrift() route_table = mkrt(ADDRESS_FAMILY_IPV6) nh1 = mknh("if1", "::1.1.1.1") nh2 = mknh("if2", "::2.2.2.2") assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Add a route # No next_hop route_table.put_route(mkr("1111:1111:1111:1111:0000:0000:0000:0000/64", N)) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Add two routes with same prefix but with different owners (most preferred added first) # One next_hop each route_table.put_route(mkr("2222:2222:2222:2222:2222:2222:2222:2222/128", S, [nh1])) route_table.put_route(mkr("2222:2222:2222:2222:2222:2222:2222:2222/128", N, [nh2])) assert route_table.nr_destinations() == 2 assert route_table.nr_routes() == 3 # Add two routes with same prefix but with different owners (most preferred added last) # Two next_hops each route_table.put_route(mkr("3333:3333:3333:3333:3333:0000:0000:0000/80", N, [nh1, nh2])) route_table.put_route(mkr("3333:3333:3333:3333:3333:0000:0000:0000/80", S, [nh1, nh2])) assert route_table.nr_destinations() == 3 assert route_table.nr_routes() == 5 # Replace route which is already in the table (only one route to prefix) route_table.put_route(mkr("4444::/16", N)) route_table.put_route(mkr("4444::/16", N)) assert route_table.nr_destinations() == 4 assert route_table.nr_routes() == 6 # Replace route which is already in the table (multiple routes to prefix) route_table.put_route(mkr("::0.0.0.0/0", S)) route_table.put_route(mkr("::0.0.0.0/0", N)) route_table.put_route(mkr("::0.0.0.0/0", N)) assert route_table.nr_destinations() == 5 assert route_table.nr_routes() == 8
def test_add_prefix_tie(): packet_common.add_missing_methods_to_thrift() test_node = make_test_node() prefix_tie_packet_1 = packet_common.make_prefix_tie_packet( direction=common.ttypes.TieDirectionType.South, originator=222, tie_nr=333, seq_nr=444) timestamp = common.ttypes.IEEE802_1ASTimeStampType(AS_sec=12345) monotonic_clock = common.ttypes.PrefixSequenceType(timestamp=timestamp) packet_common.add_ipv4_prefix_to_prefix_tie( prefix_tie_packet_1, packet_common.make_ipv4_prefix("1.2.3.0/24"), 2, [77, 88], monotonic_clock) packet_common.add_ipv6_prefix_to_prefix_tie( prefix_tie_packet_1, packet_common.make_ipv6_prefix("1234:abcd::/64"), 3) test_node.store_tie_packet(prefix_tie_packet_1, 555) prefix_tie_packet_2 = packet_common.make_prefix_tie_packet( direction=common.ttypes.TieDirectionType.North, originator=777, tie_nr=888, seq_nr=999) packet_common.add_ipv4_prefix_to_prefix_tie( prefix_tie_packet_2, packet_common.make_ipv4_prefix("0.0.0.0/0"), 10) test_node.store_tie_packet(prefix_tie_packet_2, 0) db_packet_info = test_node.find_tie_packet_info( prefix_tie_packet_1.header.tieid) db_tie_packet = db_packet_info.protocol_packet.content.tie assert db_tie_packet == prefix_tie_packet_1 db_packet_info = test_node.find_tie_packet_info( prefix_tie_packet_2.header.tieid) db_tie_packet = db_packet_info.protocol_packet.content.tie assert db_tie_packet == prefix_tie_packet_2 missing_tie_id = encoding.ttypes.TIEID( direction=common.ttypes.TieDirectionType.South, originator=321, tietype=common.ttypes.TIETypeType.PrefixTIEType, tie_nr=654) assert test_node.find_tie_packet_info(missing_tie_id) is None tab = test_node.tie_db_table() tab_str = tab.to_string() assert ( tab_str == "+-----------+------------+--------+--------+--------+----------+------------------------+\n" "| Direction | Originator | Type | TIE Nr | Seq Nr | Lifetime | Contents |\n" "+-----------+------------+--------+--------+--------+----------+------------------------+\n" "| South | 222 | Prefix | 333 | 444 | 555 | Prefix: 1.2.3.0/24 |\n" "| | | | | | | Metric: 2 |\n" "| | | | | | | Tag: 77 |\n" "| | | | | | | Tag: 88 |\n" "| | | | | | | Monotonic-clock: |\n" "| | | | | | | Timestamp: 12345 |\n" "| | | | | | | Prefix: 1234:abcd::/64 |\n" "| | | | | | | Metric: 3 |\n" "+-----------+------------+--------+--------+--------+----------+------------------------+\n" "| North | 777 | Prefix | 888 | 999 | 0 | Prefix: 0.0.0.0/0 |\n" "| | | | | | | Metric: 10 |\n" "+-----------+------------+--------+--------+--------+----------+------------------------+\n" )
def test_add_prefix_tie(): packet_common.add_missing_methods_to_thrift() test_node = make_test_node() prefix_tie_packet_1 = packet_common.make_prefix_tie_packet( direction=common.ttypes.TieDirectionType.South, originator=222, tie_nr=333, seq_nr=444, lifetime=555) packet_common.add_ipv4_prefix_to_prefix_tie( prefix_tie_packet_1, packet_common.make_ipv4_prefix("1.2.3.0/24"), 2, [77, 88], 12345) packet_common.add_ipv6_prefix_to_prefix_tie( prefix_tie_packet_1, packet_common.make_ipv6_prefix("1234:abcd::/64"), 3) test_node.store_tie_packet(prefix_tie_packet_1) prefix_tie_packet_2 = packet_common.make_prefix_tie_packet( direction=common.ttypes.TieDirectionType.North, originator=777, tie_nr=888, seq_nr=999, lifetime=0) packet_common.add_ipv4_prefix_to_prefix_tie( prefix_tie_packet_2, packet_common.make_ipv4_prefix("0.0.0.0/0"), 10) test_node.store_tie_packet(prefix_tie_packet_2) assert test_node.find_tie_meta( prefix_tie_packet_1.header.tieid).tie_packet == prefix_tie_packet_1 assert test_node.find_tie_meta( prefix_tie_packet_2.header.tieid).tie_packet == prefix_tie_packet_2 missing_tie_id = encoding.ttypes.TIEID( direction=common.ttypes.TieDirectionType.South, originator=321, tietype=common.ttypes.TIETypeType.PrefixTIEType, tie_nr=654) assert test_node.find_tie_meta(missing_tie_id) is None tab = test_node.tie_db_table() tab_str = tab.to_string() assert ( tab_str == "+-----------+------------+--------+--------+--------+----------+--------------------------+\n" "| Direction | Originator | Type | TIE Nr | Seq Nr | Lifetime | Contents |\n" "+-----------+------------+--------+--------+--------+----------+--------------------------+\n" "| South | 222 | Prefix | 333 | 444 | 555 | Prefix: 1.2.3.0/24 |\n" "| | | | | | | Metric: 2 |\n" "| | | | | | | Tag: 77 |\n" "| | | | | | | Tag: 88 |\n" "| | | | | | | Monotonic-clock: 12345 |\n" "| | | | | | | Prefix: 1234:abcd::/64 |\n" "| | | | | | | Metric: 3 |\n" "+-----------+------------+--------+--------+--------+----------+--------------------------+\n" "| North | 777 | Prefix | 888 | 999 | 0 | Prefix: 0.0.0.0/0 |\n" "| | | | | | | Metric: 10 |\n" "+-----------+------------+--------+--------+--------+----------+--------------------------+\n" )
def test_process_tire(): packet_common.add_missing_methods_to_thrift() db_tie_info_list = [ # pylint:disable=bad-whitespace # Direction Origin Type TieNr SeqNr Lifetime Disposition (SOUTH, 10, PREFIX, 13, 3, 100), # Older version than in TIRE; request (SOUTH, 10, PREFIX, 14, 2, 100), # Not in TIRE; no action (NORTH, 3, PREFIX, 15, 7, 100), # Newer version than in TIRE; start (NORTH, 4, PREFIX, 1, 8, 100) ] # Same version as in TIRE; ack test_node = make_test_node(db_tie_info_list) check_process_tire(test_node)
def test_ipv6_table_del_route(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV6) assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Try to delete a route that is not present in the table (empty table) assert not route_table.del_route(mkp("1111:1111:1111::/48"), S) assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Try to delete a route that is not present in the table (prefix is not present) route_table.put_route(mkr("2222::/16", S)) assert not route_table.del_route( mkp("3333:3333:3333:3333:3333:0000:0000:0000/80"), S) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Try to delete a route that is not present in the table (prefix length is wrong) assert not route_table.del_route(mkp("2222:2222::/32"), S) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Try to delete a route that is not present in the table (owner is not present) assert not route_table.del_route(mkp("2222::/16"), N) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Delete a route that is present in the table (only route for prefix) assert route_table.get_route(mkp("2222::/16"), S) is not None assert route_table.del_route(mkp("2222::/16"), S) assert route_table.get_route(mkp("2222::/16"), S) is None assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Put the deleted route back and add other routes to the table route_table.put_route(mkr("::/0", N)) route_table.put_route(mkr("2222::/16", N)) route_table.put_route(mkr("2222::/16", S)) route_table.put_route(mkr("2222:2222::/32", N)) route_table.put_route(mkr("3333:3333:3333:3333:3333:0000:0000:0000/80", S)) route_table.put_route(mkr("3333:3333:3333:3333:3333:3333:3333:3333/128", S)) assert route_table.nr_destinations() == 5 assert route_table.nr_routes() == 6 # Delete a route that is present in the table (multiple routes for prefix, get most preferred) assert route_table.get_route(mkp("2222::/16"), S) is not None assert route_table.del_route(mkp("2222::/16"), S) assert route_table.get_route(mkp("2222::/16"), S) is None assert route_table.nr_destinations() == 5 assert route_table.nr_routes() == 5 route_table.put_route(mkr("2222::/16", S)) # Delete a route that is present in the table (multiple routes for prefix, get least preferred) assert route_table.get_route(mkp("2222::/16"), N) is not None assert route_table.del_route(mkp("2222::/16"), N) assert route_table.get_route(mkp("2222::/16"), N) is None assert route_table.nr_destinations() == 5 assert route_table.nr_routes() == 5 route_table.put_route(mkr("2222::/16", N))
def main(): args = parse_command_line_arguments() parse_environment_variables(args) parsed_config = config.parse_configuration(args.configfile) packet_common.add_missing_methods_to_thrift() eng = engine.Engine(run_which_nodes=run_which_nodes(args), passive_nodes=args.passive_nodes, interactive=args.interactive, telnet_port_file=args.telnet_port_file, multicast_loopback=multicast_loopback(args), log_level=args.log_level, config=parsed_config) eng.run()
def test_ipv4_table_del_route(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Try to delete a route that is not present in the table (empty table) assert not route_table.del_route(mkp("1.1.1.0/24"), S) assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Try to delete a route that is not present in the table (prefix is not present) route_table.put_route(mkr("2.2.2.0/32", S)) assert not route_table.del_route(mkp("3.3.3.0/24"), S) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Try to delete a route that is not present in the table (prefix length is wrong) assert not route_table.del_route(mkp("2.2.2.0/24"), S) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Try to delete a route that is not present in the table (owner is not present) assert not route_table.del_route(mkp("2.2.2.0/32"), N) assert route_table.nr_destinations() == 1 assert route_table.nr_routes() == 1 # Delete a route that is present in the table (only route for prefix) assert route_table.get_route(mkp("2.2.2.0/32"), S) is not None assert route_table.del_route(mkp("2.2.2.0/32"), S) assert route_table.get_route(mkp("2.2.2.0/32"), S) is None assert route_table.nr_destinations() == 0 assert route_table.nr_routes() == 0 # Put the deleted route back and add other routes to the table route_table.put_route(mkr("1.1.0.0/16", N)) route_table.put_route(mkr("2.2.2.0/32", S)) route_table.put_route(mkr("2.2.2.0/32", N)) route_table.put_route(mkr("2.2.2.0/24", N)) route_table.put_route(mkr("3.0.0.0/8", S)) assert route_table.nr_destinations() == 4 assert route_table.nr_routes() == 5 # Delete a route that is present in the table (multiple routes for prefix, get most preferred) assert route_table.get_route(mkp("2.2.2.0/32"), S) is not None assert route_table.del_route(mkp("2.2.2.0/32"), S) assert route_table.get_route(mkp("2.2.2.0/32"), S) is None assert route_table.nr_destinations() == 4 assert route_table.nr_routes() == 4 route_table.put_route(mkr("2.2.2.0/32", S)) # Delete a route that is present in the table (multiple routes for prefix, get least preferred) assert route_table.get_route(mkp("2.2.2.0/32"), N) is not None assert route_table.del_route(mkp("2.2.2.0/32"), N) assert route_table.get_route(mkp("2.2.2.0/32"), N) is None assert route_table.nr_destinations() == 4 assert route_table.nr_routes() == 4 route_table.put_route(mkr("2.2.2.0/32", N))
def test_fix_node_tie_packet(): packet_common.add_missing_methods_to_thrift() tie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=None, tie=encoding.ttypes.TIEPacket( header=max_tie_header(), element=encoding.ttypes.TIEElement( node=encoding.ttypes.NodeTIEElement( level=packet_common.MAX_U16, neighbors={ max_system_id(0): max_neighbor(), max_system_id(1): max_neighbor(), max_system_id(2): max_neighbor() }, capabilities=encoding.ttypes.NodeCapabilities( protocol_minor_version=protocol_minor_version, flood_reduction=True, hierarchy_indications=common.ttypes.HierarchyIndications.leaf_only ), flags=encoding.ttypes.NodeFlags( overload=True ), name="name" ), prefixes=None, positive_disaggregation_prefixes=None, negative_disaggregation_prefixes=None, external_prefixes=None, keyvalues=None ) ) ) ) packet_info = packet_common.encode_protocol_packet(tie_protocol_packet, None) packet_info.update_env_header(0) packet_info.update_outer_sec_env_header(None, 111, 222, 10) message = b''.join(packet_info.message_parts()) decoded_packet_info = packet_common.decode_message(None, None, message, None, None, None, None) assert not decoded_packet_info.error assert packet_info.protocol_packet == decoded_packet_info.protocol_packet
def test_process_tie(): packet_common.add_missing_methods_to_thrift() db_tie_info_list = [ # pylint:disable=bad-whitespace # Direction Origin Type TieNr SeqNr Lifetime Disposition (SOUTH, 12, NODE, 3, 2, 600), # TIE_NEWER (SOUTH, 10, PREFIX, 13, 5, 100), # TIE_NEWER (SOUTH, MY_SYSTEM_ID, NODE, 1, 8, 150), # My real node TIE (SOUTH, MY_SYSTEM_ID, PREFIX, 1, 4, 69), # TIE_OLDER_SELF (NORTH, 5, PREFIX, 3, 2, 200), # TIE_OLDER (NORTH, 5, PREFIX, 8, 12, 100), # TIE_SAME (NORTH, 6, PREFIX, 7, 4, 500) ] # TIE_SAME test_node = make_test_node(db_tie_info_list) check_process_tie(test_node)
def test_fix_lie_packet(): packet_common.add_missing_methods_to_thrift() lie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=encoding.ttypes.LIEPacket( name="name", local_id=packet_common.MAX_U32, flood_port=packet_common.MAX_U16, link_mtu_size=packet_common.MAX_U32, link_bandwidth=packet_common.MAX_U32, neighbor=encoding.ttypes.Neighbor( originator=packet_common.MAX_U64, remote_id=packet_common.MAX_U32 ), pod=packet_common.MAX_U32, # nonce=packet_common.MAX_U16, # last_neighbor_nonce=packet_common.MAX_U16, node_capabilities=encoding.ttypes.NodeCapabilities( protocol_minor_version=protocol_minor_version, flood_reduction=True, hierarchy_indications=common.ttypes.HierarchyIndications.leaf_only ), link_capabilities=encoding.ttypes.LinkCapabilities( bfd=False, ), holdtime=packet_common.MAX_U16, not_a_ztp_offer=True, you_are_flood_repeater=True, label=packet_common.MAX_U32 ), tide=None, tire=None, tie=None ) ) packet_info = packet_common.encode_protocol_packet(lie_protocol_packet, None) packet_info.update_env_header(0) packet_info.update_outer_sec_env_header(None, 111, 222) message = b''.join(packet_info.message_parts()) decoded_packet_info = packet_common.decode_message(None, None, message, None, None, None, None) assert not decoded_packet_info.error assert packet_info.protocol_packet == decoded_packet_info.protocol_packet
def test_fix_node_tie_packet(): packet_common.add_missing_methods_to_thrift() tie_protocol_packet = encoding.ttypes.ProtocolPacket( header=encoding.ttypes.PacketHeader( major_version=packet_common.MAX_U16, minor_version=packet_common.MAX_U16, sender=packet_common.MAX_U64, level=packet_common.MAX_U16 ), content=encoding.ttypes.PacketContent( lie=None, tide=None, tire=None, tie=encoding.ttypes.TIEPacket( header=max_tie_header(), element=encoding.ttypes.TIEElement( node=encoding.ttypes.NodeTIEElement( level=packet_common.MAX_U16, neighbors={ max_system_id(0): max_neighbor(), max_system_id(1): max_neighbor(), max_system_id(2): max_neighbor() }, capabilities=encoding.ttypes.NodeCapabilities( flood_reduction=True, hierarchy_indications=common.ttypes.HierarchyIndications.leaf_only ), flags=encoding.ttypes.NodeFlags( overload=True ), name="name" ), prefixes=None, positive_disaggregation_prefixes=None, negative_disaggregation_prefixes=None, external_prefixes=None, keyvalues=None ) ) ) ) encoded_packet = packet_common.encode_protocol_packet(tie_protocol_packet) decoded_tie_protocol_packet = packet_common.decode_protocol_packet(encoded_packet) assert tie_protocol_packet == decoded_tie_protocol_packet
def test_age_ties(): packet_common.add_missing_methods_to_thrift() db_tie_info_list = [ # pylint:disable=bad-whitespace # Direction Origin Type TieNr SeqNr Lifetime (SOUTH, 55, NODE, 2, 4, 600), (SOUTH, MY_SYSTEM_ID, PREFIX, 18, 903, 1) ] test_node = make_test_node(db_tie_info_list) tie_id_1 = packet_common.make_tie_id(SOUTH, 55, NODE, 2) tie_id_2 = packet_common.make_tie_id(SOUTH, MY_SYSTEM_ID, PREFIX, 18) assert test_node.find_tie_packet_info(tie_id_1) is not None assert test_node.find_tie_packet_info(tie_id_2) is not None test_node.age_ties() tie_packet_info_1 = test_node.find_tie_packet_info(tie_id_1) assert tie_packet_info_1 is not None assert tie_packet_info_1.protocol_packet.content.tie.header.seq_nr == 4 assert tie_packet_info_1.remaining_tie_lifetime == 599 assert test_node.find_tie_packet_info(tie_id_2) is None
def test_cli_routes_table(): packet_common.add_missing_methods_to_thrift() kern = kernel.Kernel(log=None, log_id="", table_name="main") if not kern.platform_supported: return # We assume that the main route table always has a default route, and we look for this: # +-------------+---------+--------------------+-------------+-----------+-----------+------------+--------+ # | Table | Address | Destination | Type | Protocol | Outgoing | Gateway | Weight | # | | Family | | | | Interface | | | # +-------------+---------+--------------------+-------------+-----------+-----------+------------+--------+ # ... # | Main | IPv4 | 0.0.0.0/0 | Unicast | Boot/Dhcp | eth0 | 172.17.0.1 | | # ... tab_str = kern.cli_routes_table(254).to_string() pattern = (r"[|] Table +[|] Address +[|] Destination +[|] Type +[|] Protocol +[|] Outgoing +[|] Gateway +[|] Weight +[|]\n" r"[|] +[|] Family +[|] +[|] +[|] +[|] Interface +[|] +[|] +[|]\n" r"(.|[\n])*" r"[|] Main +[|] IPv4 +[|] 0\.0\.0\.0/0 +[|] Unicast +[|]") assert re.search(pattern, tab_str) is not None
def test_mark_and_del_stale(): packet_common.add_missing_methods_to_thrift() route_table = mkrt(constants.ADDRESS_FAMILY_IPV4) # Mark all S routes stale on an empty table assert route_table.mark_owner_routes_stale(S) == 0 # Delete all remaining stale routes (which there are none) assert route_table.del_stale_routes() == 0 # Prepare a route table with a mixture of N and S routes route_table.put_route(mkr("0.0.0.0/0", S)) route_table.put_route(mkr("1.1.1.0/24", S)) route_table.put_route(mkr("1.1.1.0/24", N)) route_table.put_route(mkr("2.2.0.0/16", S)) route_table.put_route(mkr("2.2.2.0/24", N)) # Mark all N routes stale assert route_table.mark_owner_routes_stale(N) == 2 # Overwrite an existing routes that was marked stale route_table.put_route(mkr("1.1.1.0/24", N)) # Overwrite an existing routes that was not marked stale route_table.put_route(mkr("2.2.0.0/16", S)) # Add a new route route_table.put_route(mkr("3.3.0.0/16", N)) # Delete the one remaining stale route assert route_table.del_stale_routes() == 1
def test_generate_tide_packet(): packet_common.add_missing_methods_to_thrift() db_tie_info_list = [ # pylint:disable=bad-whitespace # Direction Origin Type TieNr SeqNr Lifetime Allowed in TIDE (SOUTH, 55, PREFIX, 2, 4, 600 ), # No : Non-node S-TIE to S, not self-originated (SOUTH, MY_SYSTEM_ID, PREFIX, 18, 903, 400) ] # Yes: Non-node S-TIE to S, self-originated test_node = make_test_node(db_tie_info_list) tide_packet = test_node.generate_tide_packet( neighbor_direction=SOUTH, neighbor_system_id=55, neighbor_level=8, neighbor_is_top_of_fabric=False, my_level=MY_LEVEL, i_am_top_of_fabric=True) assert tide_packet.start_range == node.Node.MIN_TIE_ID assert tide_packet.end_range == node.Node.MAX_TIE_ID assert len(tide_packet.headers) == 1 expected_header = packet_common.make_tie_header_with_lifetime( SOUTH, MY_SYSTEM_ID, PREFIX, 18, 903, 400) assert tide_packet.headers[0] == expected_header
import re # pylint: disable=W0401 # pylint: disable=W0614 from common.ttypes import * from encoding.ttypes import * import packet_common packet_common.add_missing_methods_to_thrift() # TODO: Remove this horrible hack. # This is a horrible hack to work around the following problem. I am having huge problems getting # my visualization tool to properly correlate sent message to received messages (i.e. to properly # draw the lines from the sender to the receiver). As Tony has pointed out several times, Thrift # encoding is not canonical. On top of that, even for the exact same binary message, two different # Python processes may create different internal binary structures because Python dictionaries use # non-deterministic hashes (this is by design - it is a security feature). The following code works # around the problem: it converts a ProtocolPacket object into another object (nested tuples) which # consistently hash to the same value, regardless of the order in which iterators visit member of # dictionary or set. It does this by sorting non-deterministic containers and converting them to # tuples. I have proposed adding a correlator field the transport header to solve the problem more # fundamentally; if and when that happens we can remove this hack, and the need for packet decoding, # and we can move the visualization tool back to the tools directory. # def make_deterministic_hashable(obj, top=True): # This function produces the same has for objects that contain dictionaries and that only differ # in the order in which the dict iterators visit the members of the dict. if not top: try: hash(obj)
def test_is_flood_allowed(): packet_common.add_missing_methods_to_thrift() test_node = make_test_node() # Node 66 is same level as me node_66_tie_packet = packet_common.make_node_tie_packet(name="node66", level=MY_LEVEL, direction=SOUTH, originator=66, tie_nr=5, seq_nr=7) test_node.store_tie_packet(node_66_tie_packet, 300) # Node 77 has higher level than me node_77_tie_packet = packet_common.make_node_tie_packet(name="node77", level=MY_LEVEL + 1, direction=SOUTH, originator=77, tie_nr=3, seq_nr=2) test_node.store_tie_packet(node_77_tie_packet, 400) # Node 88 has lower level than me node_88_tie_packet = packet_common.make_node_tie_packet(name="node88", level=MY_LEVEL - 1, direction=SOUTH, originator=88, tie_nr=7, seq_nr=3) test_node.store_tie_packet(node_88_tie_packet, 400) tx_tie_info_list = [ # pylint:disable=bad-whitespace # Neighbor Neighbor I am Allowed Reason # Direction Originator Type, Tie-Nr Seq-Nr Lifetime Direction System-ID ToF (SOUTH, 66, NODE, 5, 7, 400, SOUTH, 22, False, True, "Node S-TIE to S: originator level is same as from-node"), (SOUTH, 77, NODE, 3, 2, 300, SOUTH, 18, False, False, "Node S-TIE to S: originator level is not same as from-node"), (SOUTH, 55, NODE, 8, 12, 500, NORTH, 17, False, False, "Node S-TIE to N: could not determine originator level"), (SOUTH, 77, NODE, 3, 3, 200, NORTH, 17, True, True, "Node S-TIE to N: originator level is higher than from-node"), (SOUTH, 88, NODE, 7, 3, 400, NORTH, 19, False, False, "Node S-TIE to N: originator level is not higher than from-node"), (SOUTH, 55, NODE, 8, 12, 550, EW, 19, True, False, "Node S-TIE to EW: from-node is top of fabric"), (SOUTH, 55, NODE, 8, 12, 550, EW, 19, False, True, "Node S-TIE to EW: from-node is not top of fabric"), (SOUTH, 55, NODE, 8, 12, 550, None, 19, False, False, "Node S-TIE to ?: never flood"), (SOUTH, MY_SYSTEM_ID, PREFIX, 4, 7, 200, SOUTH, 20, False, True, "Non-node S-TIE to S: self-originated"), (SOUTH, 55, PREFIX, 2, 4, 600, SOUTH, 20, True, False, "Non-node S-TIE to S: not self-originated"), (SOUTH, MY_SYSTEM_ID, PREFIX, 18, 903, 400, SOUTH, 33, True, True, "Non-node S-TIE to S: self-originated"), (SOUTH, 55, PREFIX, 2, 4, 600, NORTH, 55, False, True, "Non-node S-TIE to N: to-node is originator of TIE"), (SOUTH, 55, PREFIX, 2, 4, 600, NORTH, 33, False, False, "Non-node S-TIE to N: to-node is not originator of TIE"), (SOUTH, 55, PREFIX, 2, 4, 600, EW, 33, True, False, "Non-node S-TIE to EW: this top of fabric"), (SOUTH, MY_SYSTEM_ID, PREFIX, 18, 903, 400, EW, 33, False, True, "Non-node S-TIE to EW: self-originated and not top of fabric"), (SOUTH, 55, PREFIX, 2, 4, 600, EW, 33, False, False, "Non-node S-TIE to EW: not self-originated"), (SOUTH, 55, PREFIX, 2, 4, 600, None, 33, True, False, "None-node S-TIE to ?: never flood"), (NORTH, 55, NODE, 3, 3, 500, SOUTH, 20, False, False, "N-TIE to S: never flood"), (NORTH, 55, PREFIX, 2, 4, 500, SOUTH, 20, True, False, "N-TIE to S: never flood"), (NORTH, 55, NODE, 3, 3, 500, NORTH, 20, False, True, "N-TIE to N: always flood"), (NORTH, 55, PREFIX, 2, 4, 500, NORTH, 20, True, True, "N-TIE to N: always flood"), (NORTH, 55, NODE, 3, 3, 500, EW, 20, True, True, "N-TIE to EW: top of fabric"), (NORTH, 55, NODE, 3, 3, 500, EW, 20, False, False, "N-TIE to EW: not top of fabric"), (NORTH, 55, NODE, 3, 3, 500, None, 20, False, False, "N-TIE to ?: never flood") ] for tx_tie_info in tx_tie_info_list: (direction, originator, tietype, tie_nr, seq_nr, lifetime, neighbor_direction, neighbor_system_id, i_am_top_of_fabric, expected_allowed, expected_reason) = tx_tie_info tie_header = packet_common.make_tie_header_with_lifetime( direction, originator, tietype, tie_nr, seq_nr, lifetime) (allowed, reason) = test_node.is_flood_allowed( tie_header=tie_header.header, to_node_direction=neighbor_direction, to_node_system_id=neighbor_system_id, from_node_system_id=MY_SYSTEM_ID, from_node_level=MY_LEVEL, from_node_is_top_of_fabric=i_am_top_of_fabric) assert allowed == expected_allowed, expected_reason assert reason == expected_reason