Exemple #1
0
def test_flows_fail_from_session():
    """Confirm flows-fail assert passes and fails as expected when called from a session."""
    startLocation = "node1"
    headers = HeaderConstraints(srcIps="1.1.1.1")
    bf = Session(load_questions=False)
    with patch.object(bf.q, "reachability", create=True) as reachability:
        # Test success
        reachability.return_value = MockQuestion()
        bf.asserts.assert_flows_fail(startLocation, headers)
        reachability.assert_called_with(
            pathConstraints=PathConstraints(startLocation=startLocation),
            headers=headers,
            actions="success",
        )
        # Test failure
        mock_df = DataFrame.from_records([{"Flow": "found", "More": "data"}])
        reachability.return_value = MockQuestion(MockTableAnswer(mock_df))
        with pytest.raises(BatfishAssertException) as excinfo:
            bf.asserts.assert_flows_fail(startLocation, headers)
        # Ensure found answer is printed
        assert mock_df.to_string() in str(excinfo.value)
        reachability.assert_called_with(
            pathConstraints=PathConstraints(startLocation=startLocation),
            headers=headers,
            actions="success",
        )
Exemple #2
0
def test_filter_permits_no_session():
    """
    Confirm filter-permits assert passes and fails as expected when not specifying a session.

    For reverse compatibility.
    """
    headers = HeaderConstraints(srcIps="1.1.1.1")
    # Confirm assert works when not specifying a session
    # for reverse compatibility
    with patch.object(bfq, "searchFilters",
                      create=True) as mock_search_filters:
        # Test success
        mock_search_filters.return_value = MockQuestion()
        assert_filter_permits("filter", headers)
        mock_search_filters.assert_called_with(filters="filter",
                                               headers=headers,
                                               action="deny")
        # Test failure; also test that startLocation is passed through
        mock_df = DataFrame.from_records([{"Flow": "found", "More": "data"}])
        mock_search_filters.return_value = MockQuestion(
            MockTableAnswer(mock_df))
        with pytest.raises(BatfishAssertException) as excinfo:
            assert_filter_permits("filter", headers, startLocation="Ethernet1")
        # Ensure found answer is printed
        assert mock_df.to_string() in str(excinfo.value)
        mock_search_filters.assert_called_with(filters="filter",
                                               headers=headers,
                                               startLocation="Ethernet1",
                                               action="deny")
Exemple #3
0
def test_filter_denies_from_session():
    """Confirm filter-denies assert passes and fails as expected when called from a session."""
    headers = HeaderConstraints(srcIps="1.1.1.1")
    bf = Session(load_questions=False)
    with patch.object(bf.q, "searchFilters",
                      create=True) as mock_search_filters:
        # Test success
        mock_search_filters.return_value = MockQuestion()
        bf.asserts.assert_filter_denies("filter", headers)
        mock_search_filters.assert_called_with(filters="filter",
                                               headers=headers,
                                               action="permit")
        # Test failure; also test that startLocation is passed through
        mock_df = DataFrame.from_records([{"Flow": "found", "More": "data"}])
        mock_search_filters.return_value = MockQuestion(
            MockTableAnswer(mock_df))
        with pytest.raises(BatfishAssertException) as excinfo:
            bf.asserts.assert_filter_denies("filter",
                                            headers,
                                            startLocation="Ethernet1")
        # Ensure found answer is printed
        assert mock_df.to_string() in str(excinfo.value)
        mock_search_filters.assert_called_with(
            filters="filter",
            headers=headers,
            startLocation="Ethernet1",
            action="permit",
        )
Exemple #4
0
def test_customer_link_input_filter_anti_spoofing(bf, customer_list, bgpPeer):
    """Check that all customer input filters only allowed specified source IP addresses"""
    bf.asserts.current_assertion = 'Assert all customer input filters only allow specified source IP addresses'

    missing = []
    for customer in customer_list:
        df = bf.q.resolveInterfaceSpecifier(
            interfaces=f"@connectedTo({customer['Remote_IP']})",
            nodes=customer['Node']).answer().frame()
        # this only works for numbered interfaces and BGP peers
        if not df.empty:
            local_intf = df.iloc[0].Interface
        else:
            missing.append(
                f"Bad specifier: @connectedTo({customer['Remote_IP']}, Cannot find local interface for {customer}"
            )
            continue

        df2 = bf.q.interfaceProperties(
            interfaces=str(local_intf)).answer().frame()

        if df2.empty:
            missing.append(
                f"No Interface Properties for {local_intf}, Cannot find local interface for {customer}"
            )
            continue

        input_acl = df2.iloc[0]['Incoming_Filter_Name']

        if df2.iloc[0]['Incoming_Filter_Name'] is None:
            missing.append(
                f"No ACL applied to {local_intf} connecting to {customer}")
            continue

        in_acl = df2.iloc[0]['Incoming_Filter_Name']
        filter_spec = f"@in({df2.iloc[0]['Interface']})"
        srcIps = ",".join(customer.get('Origin_Prefixes', ["0.0.0.0/0"]))
        headers = HeaderConstraints(srcIps=srcIps)
        df3 = bf.q.searchFilters(action='permit',
                                 headers=headers,
                                 filters=filter_spec).answer().frame()

        if df3.empty:
            continue
        else:
            missing.append(
                f"ACL {in_acl} on {local_intf} connecting to {customer} does not prevent spoofing\n\n{df3}\n"
            )

    # need to check anti-spoofing here

    test = (len(missing) == 0)
    print_fail = "\n".join(missing)
    pass_message = "All customer input filters prevent spoofing\n"
    fail_message = f"List of customer connections with filters that do not prevent spoofing\n{print_fail}"

    record_results(bf, test, pass_message, fail_message)
Exemple #5
0
def get_traces(nodes: List[str], snapshot: str):
    traces = {}
    for n1 in nodes:
        for n2 in nodes:
            results = bfq.traceroute(
                startLocation=n1, headers=HeaderConstraints(dstIps=n2)).answer(
                    snapshot=snapshot).frame()
            for idx, result in results.iterrows():
                for trace in result.Traces:
                    if trace.disposition not in traces:
                        traces[trace.disposition] = set()
                    hops_str = json.dumps(convert_list_to_tuple(trace.hops))
                    traces[trace.disposition].add(hops_str)
    return traces
def test_vtep_reachability(bf, l2_vnis):
    """Check that all VTEPs can reach each other."""
    bf.asserts.current_assertion = 'Assert all VTEP reachability'

    # Collect the list of VTEP_IP from vxlanVniProperties and then loop
    # through to do traceroute
    vtep_ip_list = set(l2_vnis['Local_VTEP_IP'])
    vni_dict = defaultdict(dict)

    for index, row in l2_vnis.iterrows():
        node = row['Node']
        t_ip = row['Local_VTEP_IP']
        vni_dict[node]['Local_IP'] = t_ip
        for vtep_ip in vtep_ip_list:
            if vtep_ip != t_ip:
                try:
                    vni_dict[node]['Remote_IPs'].add(vtep_ip)
                except KeyError:
                    vni_dict[node]['Remote_IPs'] = {vtep_ip}

    tr_dict = defaultdict(list)

    for src_location in vni_dict.keys():
        for remote_vtep in vni_dict[src_location]['Remote_IPs']:
            src_ip = vni_dict[src_location]['Local_IP']
            headers = HeaderConstraints(srcIps=src_ip,
                                        dstIps=remote_vtep,
                                        ipProtocols='udp',
                                        dstPorts='4789')
            tr = bf.q.traceroute(startLocation=src_location +
                                 "[@vrf(default)]",
                                 headers=headers).answer().frame()
            for trace in tr.Traces[0]:
                if trace.disposition != 'ACCEPTED':
                    tr_dict[f"{src_location}:{src_ip}"].append(remote_vtep)

    test = not len(tr_dict)
    pass_message = 'Full VTEP to VTEP reachability\n'
    fail_message = f"Some VTEPs unable to reach others:\n{dict(tr_dict)}"

    record_results(bf, test, pass_message, fail_message)
Exemple #7
0
def test_filter_denies():
    headers = HeaderConstraints(srcIps='1.1.1.1')
    with patch.object(bfq, 'searchFilters',
                      create=True) as mock_search_filters:
        # Test success
        mock_search_filters.return_value = MockQuestion()
        assert_filter_denies('filter', headers)
        mock_search_filters.assert_called_with(filters='filter',
                                               headers=headers,
                                               action='permit')
        # Test failure; also test that startLocation is passed through
        mock_df = DataFrame.from_records([{'Flow': 'found', 'More': 'data'}])
        mock_search_filters.return_value = MockQuestion(
            MockTableAnswer(mock_df))
        with pytest.raises(BatfishAssertException) as excinfo:
            assert_filter_denies('filter', headers, startLocation='Ethernet1')
        # Ensure found answer is printed
        assert mock_df.to_string() in str(excinfo.value)
        mock_search_filters.assert_called_with(filters='filter',
                                               headers=headers,
                                               startLocation='Ethernet1',
                                               action='permit')
Exemple #8
0
def test_flows_succeed():
    """Confirm flows-succeed assert passes and fails as expected when specifying a session."""
    startLocation = "node1"
    headers = HeaderConstraints(srcIps='1.1.1.1')
    bf = Session(load_questions=False)
    with patch.object(bf.q, 'reachability', create=True) as reachability:
        # Test success
        reachability.return_value = MockQuestion()
        assert_flows_succeed(startLocation, headers, session=bf)
        reachability.assert_called_with(
            pathConstraints=PathConstraints(startLocation=startLocation),
            headers=headers,
            actions='failure')
        # Test failure
        mock_df = DataFrame.from_records([{'Flow': 'found', 'More': 'data'}])
        reachability.return_value = MockQuestion(MockTableAnswer(mock_df))
        with pytest.raises(BatfishAssertException) as excinfo:
            assert_flows_succeed(startLocation, headers, session=bf)
        # Ensure found answer is printed
        assert mock_df.to_string() in str(excinfo.value)
        reachability.assert_called_with(
            pathConstraints=PathConstraints(startLocation=startLocation),
            headers=headers,
            actions='failure')
Exemple #9
0
@pytest.fixture(scope="module")
def session_fail():
    s = Session()
    name = s.init_snapshot(join(_this_dir, "snapshots", "asserts_fail"))
    yield s
    s.delete_snapshot(name)


@pytest.mark.parametrize(
    "assert_func, params",
    [
        (
            "assert_filter_denies",
            {
                "filters": "/101/",
                "headers": HeaderConstraints(srcIps="12.34.56.78"),
                "startLocation": "@enter(node[GigabitEthernet1/0])",
            },
        ),
        ("assert_filter_has_no_unreachable_lines", {
            "filters": "/101/"
        }),
        (
            "assert_filter_permits",
            {
                "filters": "/101/",
                "headers": HeaderConstraints(srcIps="1.0.1.0",
                                             dstIps="8.8.8.8"),
                "startLocation": "@enter(node[GigabitEthernet1/0])",
            },
        ),
Exemple #10
0
    s.delete_snapshot(name)


# Just use a single session and snapshot for all failing assertion tests
@pytest.fixture(scope='module')
def session_fail():
    s = Session()
    name = s.init_snapshot(join(_this_dir, 'snapshots', 'asserts_fail'))
    yield s
    s.delete_snapshot(name)


@pytest.mark.parametrize('assert_func, params', [
    ('assert_filter_denies', {
        'filters': '/101/',
        'headers': HeaderConstraints(srcIps='12.34.56.78'),
        'startLocation': '@enter(node[GigabitEthernet1/0])',
    }),
    ('assert_filter_has_no_unreachable_lines', {
        'filters': '/101/',
    }),
    ('assert_filter_permits', {
        'filters': '/101/',
        'headers': HeaderConstraints(srcIps='1.0.1.0', dstIps='8.8.8.8'),
        'startLocation': '@enter(node[GigabitEthernet1/0])',
    }),
    ('assert_flows_fail', {
        'startLocation':
        '@enter(node[GigabitEthernet1/0])',
        'headers':
        HeaderConstraints(
_this_dir = abspath(dirname(realpath(__file__)))
_root_dir = abspath(join(_this_dir, pardir))
sys.path.insert(0, _root_dir)

_questions_to_ignore_for_columns = [
    "filterTable",  # takes a question as input; no fixed schema
    "testRoutePolicies",  # likely to evolve; need work to support its types
    "viModel"  # not a table answer
]

_example_snapshot_dir = join(_root_dir, 'jupyter_notebooks/networks/example')

# Example values to use for parameters of a given type
_values_by_type = {
    'filterSpec': '/.*/',
    'headerConstraint': HeaderConstraints(dstIps="0.0.0.0/0"),
    'interfacesSpec': '/.*/',
    'ip': "1.1.1.1",
    'ipSpaceSpec': '0.0.0.0/0',
    'locationSpec': "/.*/",
    'nodeSpec': "/.*/",
}

# mapping from question category tags to category name and description
# the master list of categories being checked and enforced is here: https://github.com/batfish/batfish/blob/master/tests/questions/test_questions.py
_question_categories = OrderedDict([
    ("configuration", [
        "Configuration data questions",
        "Questions that return the contents of configuration in a structured format."
    ]),
    ("hygiene", [