...
 
Commits (3)
#!/usr/bin/env python3
# Copyright (C) 2019 Simon Redman <sredman@cs.utah.edu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ssh_helper
import argparse
import networkx
from typing import List
"""
Remove the ipt_NETFLOW module if it exists and then insert it with the new parameters
"""
MODPROBE_TEMPLATE = "sudo modprobe -rq ipt_NETFLOW; sudo modprobe ipt_NETFLOW destination={collector_ip}:{port} protocol=9"
IPTABLES_LINE_TEMPLATE = "sudo ip6tables -D {table} -j NETFLOW 2>/dev/null; sudo ip6tables -I {table} -j NETFLOW"
def _write_iptables_commands(router_sessions, iptables_commands: List[str]) -> None:
ssh_helper.run_commands_on_many_hosts(router_sessions, iptables_commands)
def _write_modprobe_commands(router_sessions, modprobe_commands: List[str]) -> None:
ssh_helper.run_commands_on_many_hosts(router_sessions, modprobe_commands)
def _build_iptables_lines(number: int) -> List[str]:
"""
Build the iptables to collect all netflow traffic
:param number: Build this many copies. There is nothing node-unique about the iptables command.
"""
line = " && ".join([IPTABLES_LINE_TEMPLATE.format(
table=table,
) for table in ["INPUT", "OUTPUT", "FORWARD"]])
return [line] * number
def _build_modprobe_lines(netgraph: networkx.Graph, port_nums: List[int], collector_node: str) -> List[str]:
"""
Construct a command line to load ipt_NETFLOW for each border router with the port number it
should send data to
"""
collector_ip = netgraph._node[collector_node]['management-ip']
lines: List[str] = [MODPROBE_TEMPLATE.format(
collector_ip=collector_ip,
port=port,
) for port in port_nums]
return lines
def _get_port_nums(netgraph: networkx.Graph, border_routers: List[str]) -> List[int]:
"""
Read the 'sensor_port' property for each border_router in the netgraph
"""
port_nums: List[int] = [netgraph._node[router]['sensor_port'] for router in border_routers]
return port_nums
def configure(netgraph: networkx.Graph, collector_node: str, border_routers: List[str]) -> None:
"""
Configure the controller node to be a SiLK NetFlow v9 collector
MODIFIES netgraph to have the listening port information needed by ipt_NETFLOW_configurator
:param netgraph: networkx graph object representing the network
:param collector_node: Node which is running the SiLK collector as represented in the graph
:param border_routers: List of nodes to whom we are listening
:return: Output from the SSH commands
"""
router_sessions = [netgraph._node[node]['session'] for node in border_routers]
port_nums: List[int] = _get_port_nums(netgraph, border_routers)
modprobe_lines: List[str] = _build_modprobe_lines(netgraph, port_nums, collector_node)
iptables_lines: List[str] = _build_iptables_lines(len(border_routers))
_write_modprobe_commands(router_sessions, modprobe_lines)
_write_iptables_commands(router_sessions, iptables_lines)
if __name__ == "__main__":
parser = argparse.ArgumentParser("Configure the ipt_NETFLOW on border routers to send data to the central collector")
args = parser.parse_args()
print("This library is not currently executable")
......@@ -19,7 +19,7 @@
import add_routable_ipv6_addrs
import frr_configurator
import json
import ipt_NETFLOW_configurator
import ospf_sniffer_configurator
import silk_configurator
import ssh_helper
......@@ -28,6 +28,7 @@ import topomap_parser
import argparse
import getpass
import json
from netdiff import NetJsonParser
import re
......@@ -105,6 +106,11 @@ if __name__ == "__main__":
controller_node=controller_node,
border_routers=border_nodes,
)
ipt_NETFLOW_configurator.configure(netgraph.graph,
collector_node=controller_node,
border_routers=border_nodes,
)
ssh_helper.network_graph_logout(netgraph.graph)
if args.netgraph_write:
......
......@@ -24,7 +24,7 @@ from typing import List, Optional
import ssh_helper
DEFAULT_CLONE_PATH="~/sniffer/"
DEFAULT_CLONE_PATH="/tmp/sniffer/"
DEFAULT_CONTROLLER='core0'
DEFAULT_CONTROLLER_PORT = 8080 # While this could theoretically be any port, the SDN controller does not accept it as a parameter...
DEFAULT_EXECUTABLE='main.py'
......
......@@ -40,7 +40,7 @@ description <str>: Some optional description of the sensor
"""
SILK_CONF_SENSOR_LINE_TEMPLATE="sensor {uuid} {name} \"{description}\""
FILE_PUSH_COMMAND_TEMPLATE= "sudo mkdir -p /data/ && sudo chown $USER /data && cat <<EOF >/data/{filename}\n{data}\nEOF"
FILE_PUSH_COMMAND_TEMPLATE= "sudo mkdir -p /{path}/ && sudo chown $USER /{path}/ && cat <<EOF >/{path}/{filename}\n{data}\nEOF"
SENSOR_CONF_PROBE_BLOCK_TEMPLATE="""probe {name} netflow-v9
listen-on-port {portnum}
......@@ -58,13 +58,14 @@ end group"""
This group block describes all IPv4 addresses
"""
SENSOR_CONF_GROUP_IPV4_BLOCK="""group ipv4
ipblocks 0.0.0.0/0
ipblocks 0.0.0.0/1
ipblocks 128.0.0.0/1
end group"""
SENSOR_CONF_SENSOR_BLOCK_TEMPLATE="""sensor {name}
netflow-v9-probes {name}
internal-ipblocks @expt-network
discard-when @ipv4 # Discard ALL IPv4 traffic
discard-when source-ipblocks @ipv4 # Discard ALL IPv4 traffic
external-ipblocks remainder
end sensor"""
......@@ -105,7 +106,7 @@ def _write_sensors_conf(session, sensor_lines: List[SensorLine], port_nums: List
sensors_conf = "\n\n".join(probes + groups + sensors)
command = FILE_PUSH_COMMAND_TEMPLATE.format(filename="sensors.conf", data=sensors_conf)
command = FILE_PUSH_COMMAND_TEMPLATE.format(filename="sensors.conf", path="/data/", data=sensors_conf)
ssh_helper.run_command_on_host(session, command)
......@@ -129,7 +130,7 @@ def _write_silk_conf(session, sensor_lines: List[SensorLine]) -> None:
sensor_names=sensor_names,
)
command = FILE_PUSH_COMMAND_TEMPLATE.format(filename="silk.conf", data=silk_conf)
command = FILE_PUSH_COMMAND_TEMPLATE.format(filename="silk.conf", path="/data/", data=silk_conf)
ssh_helper.run_command_on_host(session, command)
......@@ -172,7 +173,7 @@ def configure(netgraph: networkx.Graph, controller_node: str, border_routers: Li
MODIFIES netgraph to have the listening port information needed by ipt_NETFLOW_configurator
:param netgraph: networkx graph object representing the network
:param controller_node: Hostname of node which is running the SiLK collector
:param controller_node: Node which is running the SiLK collector as represented in the graph
:param border_routers: List of nodes to whom we are listening
:return: Output from the SSH commands
"""
......