Cover V14, i01
jan2005.tar

IP Policy Enforcement with Netinfo

Stefanos Harhalakis

A common problem faced by systems administrators who don't have complete management of their network is IP policy enforcement. There are a lot of tools for monitoring routers and switches (referred as network devices), but there are few for monitoring the leaf nodes and their status.

It is not uncommon to find that there is a duplicate IP in a network or that a user has changed his IP address or even the location of his computer. (Here, "location" describes the switch port of a user and not the actual physical location.) An administrator may need to spend a couple of hours to actually resolve a duplicate IP issue because he has to find both machines online.

Common policy enforcement techniques utilize MAC address databases where each machine must be registered to be allowed to access the network from specific switch ports. This solves the problem in a tightly managed network but cannot prevent intruders from abusing public access places. It is also almost impossible (and very time consuming) to create and maintain a valid up-to-date MAC address database.

Generally there are three entities comprising a network policy:

  • IP addresses
  • MAC addresses
  • Switch ports

With the introduction of Layer 3 switches, a "switch port" is not different from a "router port". It is more convenient to refer to these as network device ports and distinguish them as:

  • Switched ports
  • Routed ports

Typically, an administrator will need to define restrictions on various combinations of these entities. For example, it may be desirable to restrict a particular MAC address to a specific switch port or range of ports or to bind a MAC address to a particular IP address. Similarly, the administrator may want to restrict a particular IP address to a specific set of ports.

This description can be represented with a triangle with IP, MAC, Port as corners and a two-way arrow on each edge:

Here an IP address restricts the MAC address, a MAC address restricts the IP address, a MAC address restricts the Port, a Port restricts the MAC address, and an IP address restricts the Port or a port restricts the IP address. It must be understood that IP->MAC is not the same as MAC->IP because an IP address may be used by one MAC address only, but a MAC address may be allowed to use more than one IP addresses.

Here are some examples that illustrate these restrictions:

  • A user has her computer on a switched port (let's say FastEthernet 1). The administrator wants to allow this user to change her computer without being bothered, so he associates the port address to only allow access to this user's IP address. If this user ever changes her IP address, the administrator will be notified. Note that the user will be able to change her NIC. We may also want to restrict this IP address to only be allowed to be used at this switched port.
  • A user owns a laptop computer and has a configured address for it. Now this laptop can be found in a lot of places; DHCP may help here, but sometimes we want to reserve special addresses for people so we must maintain another IP<->MAC database in the DHCP configuration. We simply allow this user to have a fixed IP address, and we associate his MAC address to his IP address and vice versa. This way no one else will be able to use this IP address, and this MAC address will use only this IP address.
Policy Enforcement

Enforcing the policy consists of:

  • Monitoring the network and collecting data.
  • Processing data and detecting violations.
  • Performing actions for detected violations.

There can be two approaches to policy enforcement. First, you can prevent violations from happening. This technique is quite hard to be implemented and administered. It also does not inform the administrator of violations (most of the time). Second, you can detect violations and perform some actions, either by hand or by means of scripts/programs.

Experience shows that policy "enforcement" is not always required, however, it is often more convenient simply to monitor policy violations instead of enforcing.

Users who violate network policy must not be considered as intruders. In fact there are cases where it is more important for a user to have network access even if he has violated the policy rules. After monitoring our network for about two years, we found that abuses made up about 1% of the total number of violations detected. Allowing a user who has performed a violation to have network access also helps in case of false alarms and administration mistakes.

The MAC<->IP restrictions are less frequently used when we have the MAC<->Port and IP<->Port options. This should be expected since a MAC address database for 1000+ machines is very hard to create and maintain. There are also cases where machines are discarded and stale entries remain in the database.

Collecting the Information

Collecting the information can be quite easy as long as the hardware supports it. Here I will focus on Cisco devices, but this technique can be applied to other hardware vendors, too. We're going to collect information using SNMP so it must be enabled to network devices (more on this later).

We'll use PostgreSQL to store the information. For our purposes, we will need to create tables to hold the data and will also need to timestamp them. Timestamping the collected data is essential because we want our system to remember what happened. Creating a simple program that only knows the current image is easier but it leads to great troubles. There are cases where devices are being shut off or are inaccessible that can mess up our image of the network.

We need to have a table for all the devices that we're going to monitor in our network (referred to as monitored network devices or monitored devices). This table holds the IP address of each device (routers and switches), the community string, a unique ID (we should not use the IP address for performance issues), and a convenient name.

Before beginning, we need to know which interfaces are available to each monitored network device so that we can map users to interfaces later. We do this by fetching IF-MIB::ifDescr and IF-MIB::ifAlias for each device, which will give us names and descriptions for each interface. We store this information in a table that contains a unique ID for each entry, a device ID, the ifindex value of each interface, the interface name, and the interface description.

We also need to collect the following:

  • IF-MIB::ifPhysAddress, which holds the MAC addresses of each interface.
  • IP-MIB::ipAdEntIfIndex, which holds the IP addresses that are configured on each interface. Such an entry indicates that a port is a routed port so we mark it appropriately. We combine this information with IP-MIB::ipAdEntNetMask and now we know which IP networks are available in our network. This is very useful because we will not have to manually update a list of our IP networks. This solves a problem when Proxy ARP is implemented and the ARP tables are filled with useless (for our purposes) information.
  • mib-2.17.1.4.1.2, which has a mapping of ifIndex with an internal interface number. It is required for Cisco devices only.
  • enterprises.9.9.46.1.3.1.1.3.1, which returns a list of all the configured VLANs in the switch. This is required for Cisco devices because they need to be queried for each VLAN as if they were different devices using a community name in the form of XXXX@vlanID where XXXX is the community name. Requesting information for public will return information about vlan1 only, so we need to perform another request using public@2 if there is a VLAN2 configured.
  • IF-MIB::ifType, which holds the IANA interface type ID. This is required because we want to ignore some interfaces and mask out VLANs.

Next, we want to know which IP addresses are being used by each MAC address in our network. This can be done by collecting all the ARP tables (let's say every 15 minutes) using IP-MIB::ipNetToMediaPhysAddress. We want the ARP tables from routers because they hold associations for all the IPs that are known on each routed port. Since switches can be Layer 3 and can have routed ports, we are forced to collect the ARP tables from them, too. For each ARP entry we collect, we update our database by storing the new IP-MAC pairs and updating existing ones. ARP tables can have only one entry per IP address so we will use the IP address as the primary key of that table.

We also want to know where each MAC address is located. For Cisco switches this is known as "mac-address-table" (for switches running IOS) or "content addressable memory (CAM)" (for switches running CatOS). Both of these map a MAC address to an interface that is known by fetching mib-2.17.4.3.1.2 and returning the MAC address to port assignments. We also need to get mib-2.17.4.3.1.3, which tells us how each MAC address is known (learned, security, internal, etc.).

This information by itself is all we need to know about our network. The catch here is that when we have something like:

Switch1 (port Fa0/1) <---> (port Fa0/1) Switch2 <---> 10 machines

The MAC addresses of the 10 machines are known to Switch1 and Switch2 also. Switch1 will report them as being behind its Fa0/1, and Switch2 will report them in their correct location. In this case, we ignore Switch1 and use the information from Switch2 because it has more details. Since we're monitoring both switches, we know what the internal MAC addresses of Switch2 are (via IF-MIB::ifPhysAddress and mib-2.17.4.3.1.3).

By combining this information, we can find out that port Fa0/1 of Switch1 has another monitored device behind it. For the sake of simplicity, we will assume that all monitored devices are connected using point-to-point links (with no hubs or other non-monitored devices between them). Now we can filter out all information about Fa0/1 of Switch1. This applies only to non-routed interfaces.

We store this information in a table by itself that says that MAC address X is in interface Y, where Y is an ifid that can be used to look up in the interfaces table and return the device and interface name/description.

Figure 1 is the complete database schema that is used to store this information. A table named "netdevs" contains all the devices we are going to scan. This table is filled and maintained by us. Its fields are: a device ID, the IP address, a name, the community, and a timestamp that indicates when we got data from this device (this can help to detect devices that are not reachable).

There is also a table named "interfaces", which holds the discovered interfaces of the network. Here we have a unique ID, the device ID indicating which device this interface corresponds to, the ifDescr, the ifAlias, and the ifType. There are also two timestamps, as follows:

ts: This timestamp indicates when this interface was last detected. This is used to expire interfaces. There are times when a device is not reachable or a network error occurs during the SNMP communication, so we don't want to remove interface entries the first time they are not detected. Using a timestamp, we can expire interfaces after a number of days.

last_had_netdev: This timestamp indicates when a monitored network device was last detected on this port. Just as with "ts", we want to hold a timestamp and not a simple flag, because the connected device may become unavailable. This kind of instability will lead to an incorrect network view, cause false alarms, and mess up the recorded history of the network.

Next, the "macs" table lists all the discovered MAC addresses of our network. For each entry, there is the interface where this MAC entry was last seen and a timstamp.

The "ips" table stores all discovered ARP entries (ip, mac) with a timestamp (ts), and the "subnets" table stores all discovered subnets. The "history" table is explained later.

All tables except "netdevs" have a "flags" field, which is used to store various flags. There are also some fields named "shortdesc", "comments", or "description", which hold user notes and names.

Combining the Information

Now we can combine the collected data and see that IP address I is being used by MAC address M, which is being served by interface F. This information by itself is a great boost in our administration efforts. We can now view all our monitored devices and see which IP address and MAC addresses are on each interface (Figure 2). We can also find out who owns MAC address M and where that user is located (Figure 3).

Next, we need to store the history of our network. We create a table that holds IP:MAC:Interface pairs and a timestamp for each one. Every time we collect data, we update this table by inserting new entries and updating the timestamp of existing ones. This way we can easily find out which IP addresses were used by MAC address M in the past or which MAC addresses used IP address I (Figure 4). We can also locate duplicate IPs (Figure 5).

Creating Rules

We want to implement a schema that can hold the policy of our network. We do this by populating the collected data with more information (descriptions, notes, flags) and creating three more tables to hold the IP-MAC, IP-Interface, and MAC-Interface associations. Those are enough for all the six types of rules that were described.

We populate those tables with information regarding what is allowed for each entity and we set some flags. For example, we edit an interface and say that it should serve only MAC addresses M1, M2, and M3, and that only IP addresses I1 and I2 can appear there. This is done by inserting Interface-MAC and IP-Interface associations in the appropriate tables and by setting two flags that say that this interface should serve only the associated IPs and MACs.

Detecting Violations

Now we can validate our existing (and future) IP:MAC:Interface pairs against our policy and find out about intruders (Figure 6). Using PostgreSQL capabilities, we can create functions and views that return the violations with a single SELECT statement. Most of the job is done within the database schema using functions and custom views so the user interface only needs to be an interface and nothing more.

Policy Enforcement

All violations are associated with the MAC address that corresponds to the user who violated the policy. If we're going to enforce the policy, we will need to revoke network access from users that performed violations. To do this, we create another table named "blacklist", which will hold the MAC addresses of users in violation of policy and possibly a reason. This table is populated as the final phase of the collection. This table can also be populated by hand so that the administrator can manually lock out a user.

To perform actions on Cisco devices, we must telnet to them and perform an "enable". So, we need to know the telnet username/password and the enable password. The device table is extended to hold this information.

Now, we have come to a point where we want to lock out users from our network. We can do this by shutting down their switch ports but that is not acceptable when there is more than one user behind each port. We need to add a kind of access list on those devices to prevent them from accessing the network. Cisco switches can have a list of MAC addresses that are allowed behind each port (known as the "mac-address-table"), which can be configured by command prompt. For security reasons, we also don't consider adding MAC address tables to interfaces that are known to have other monitored network devices connected.

Locking out users can introduce problems so we want to restrict this as much as possible. We want to detect policy violations, but we want to perform policy enforcement only in some places. Thus, we need to flag interfaces that will be accessed during policy enforcement by introducing a new flag for each interface. We also add a flag for each monitored network device so that we can disable policy enforcement on a device as a whole.

There are two policy enforcement methods to be applied:

  • On interfaces where we only allow a specific MAC address, we create a MAC address table and configure it without waiting for a violation to happen. If there is a user in this MAC address table who performed a violation then his or her MAC address will be removed during the next run.
  • On all other interfaces, we wait for a policy violation to happen. Then we can get a list of all the known MAC addresses existing behind a given interface and add a MAC address table to it containing all those MACs except from the violators. This is a great relief for network administrators because they don't need to know which MAC addresses are behind each interface. After the MAC address table is added in the configuration, only those users will be able to access the network.

A policy enforcement tool can be created using simple shell script commands. I've created a simple shell+expect script that reads data from the blacklist table and tries to enforce the policy as follows:

  • It determines which devices require locking.
  • For each device, it telnets and removes the old configurations. Then it creates the new one.

In fact, the script tries to be a little more clever, so it creates a list of expect scripts on each run. Then it compares that list with the previous one and, if there is a change, it will run. It will also run if a certain amount of time has passed. This way there will be no extra load on switches.

Data Expiration

One tricky aspect is knowing when to remove entries from the database. We have the current image and a history table. We also have rules, notes, and descriptions that can become outdated in a couple of years. Discovered MAC addresses may not be needed after a couple of months.

To ease the administration of such a system, we expire data based on timestamps. If after X months MAC address M is no longer being used, we can remove it from the system. At this time, we can also remove any policy data associated with the address. After all, if the address is being purged, we no longer care that it's owned by user "Goofy" or bound to a particular IP address or port. This simple timeout algorithm goes a long way toward making the tool self-maintaining. With this approach, even for very large networks (say 100,000 MAC addresses) that are monitored for a couple of years, the database will not become very large and the performance will be pretty nice.

For a sample of 58 monitored network devices we have found 1400 interfaces. There are about 2100 unique IP addresses and 2600 known MAC addresses. The MAC addresses are more than the IP addresses because the switches have a lot of internal MACs (at least one for each interface), and there are other (not monitored) switches, too.

In case of malicious activity where someone tries to fill the mac-address-table by flooding the network with fake MAC addresses to make sniffing possible, a lot of new MAC addresses will be discovered. In one such case, there were more than 30,000 MAC addresses reported for a switch port and stored to the database. Because of the expiry, however, those entries were auto-removed a couple of months later.

The database access should be pretty fast even for very large networks. On a P3/866, it takes about 400 ms to combine all the data and produce a complete current view from within a quite complex PostgreSQL view (using "explain analyze select * from view_current" in the Netinfo database).

Netinfo

The algorithm and implementation described here is included in Netinfo v3.0, which is free software distributed under the General Public License. In fact, Netinfo does some more complex computations and provides other (not well-tested) functions like "Hunting", which can be used to insert rules for interfaces that have monitored network devices connected on them. These rules can then be auto-applied to all interfaces of all monitored network devices existing behind this interface.

The whole idea has gone through a lot of stages of patching and rewriting, and I consider it to be very stable. At first, this method was created as a monitoring tool to map IPs, MACs, and Interfaces for the Technological Education Institute of Thessaloniki in Greece. Later, it was extended to have a history and hold some policy rules and to perform user lock-out. Then, it was rewritten from scratch to resolve database management and performance issues (and more).

An administrator can add descriptions to subnets, IPs, MACs, or interfaces, which will be shown on each view. The program also knows about NIC vendors and determines them from the MAC address prefix.

Stefanos Harhalakis is a graduate of the Department of Informatics of Technological Educational Institute (TEI) of Thessaloniki, Greece. He works as a Systems and Network Administrator for the Dept. of Informatics and provides support to the Institute's NOC, and his activities include development of C/C++ applications and facilities for network security and monitoring. Stefanos can be reached at: v13@it.teithe.gr.