In the previous blog post I explained and configured DHCP Snooping. This time I’ll configure both Dynamic ARP inspection and IP Source Guard. This is a logical follow up, because they share some attributes.

Both these technologies are part of the security toolbox for Layer 2 networks. They make use of the DHCP Snooping Binding Table, so that’s why I will be using our last topology to add on these two technologies.

I won’t be using the full topology this time. I will not be doing anything with the ‘Rogue DHCP Server’. The rest will be used to show how it fully works. This is the topology:

DHCP Snooping - initial

Dynamic ARP Inspection

As mentioned, I will be using the same configuration used in the post DHCP Snooping explained. While configuring, we’ll leave out bits of configuration to show how it works exactly.

This topology has the following properties:

  • The middle ‘Router’ is the DHCP server.
  • The ‘Left’ and ‘Right’ switches are DHCP snooping enabled and Dynamic ARP Inspection enabled.

First a bit of theory. If you have not yet done so, I recommend you to read through my blog post about DHCP Snooping, because using Dynamic ARP Inspection relies upon the DHCP Snooping Binding Table. The way this table operates, is explained in DHCP Snooping explained.

As with DHCP Snooping, a specific type of traffic (DHCP for DHCP Snooping and ARP for Dynamic ARP Inspection) is only allowed if the destination address you’re trying to reach, is listed in the DHCP Snooping table. An exception to this is your uplink, which you must explicity trust. This trust command differs in configuration between DHCP Snooping and Dynamic ARP Inspection, but the intention of the command is the same; to allow traffic which would otherwise be invalid.

What follows now is the basic connectivity configuration. This already includes the DHCP Snooping configuration, since this is a prerequisite for Dynamic ARP Inspection.

Router:

conf t
ip dhcp pool SERVER1
 network 192.168.1.0 255.255.255.0
 default-router 192.168.1.1 
 lease 0 0 5
!
ip dhcp pool SERVER2
 network 192.168.2.0 255.255.255.0
 default-router 192.168.2.1 
 lease 0 0 5
!
interface GigabitEthernet0/1
 no ip address
 duplex auto
 speed auto
 media-type rj45
!
interface GigabitEthernet0/1.100
 encapsulation dot1Q 100
 ip address 192.168.1.1 255.255.255.0
!
interface GigabitEthernet0/2.200
 encapsulation dot1Q 200
 ip address 192.168.2.1 255.255.255.0

Left:

conf t
vlan 100
ip dhcp snooping
ip dhcp snooping vlan 100

!
interface GigabitEthernet0/1
 description SERVER1
 switchport access vlan 100
 switchport mode access
 media-type rj45
 negotiation auto
!
interface GigabitEthernet0/2
 description DHCP Server
 switchport trunk allowed vlan 100
 switchport trunk encapsulation dot1q
 switchport mode trunk
 media-type rj45
 negotiation auto
 ip dhcp snooping trust

Right:

conf t
vlan 200
ip dhcp snooping
ip dhcp snooping vlan 200
!
interface GigabitEthernet0/1
 description DHCP Server
 switchport trunk allowed vlan 200
 switchport trunk encapsulation dot1q
 switchport mode trunk
 media-type rj45
 negotiation auto
 ip dhcp snooping trust
!
interface GigabitEthernet0/2
 description SERVER2
 switchport access vlan 200
 switchport mode access
 media-type rj45
 negotiation auto

Now we’ll ensure that ‘PC1’, ‘Spoofed PC’ and ‘PC2’ receive an IP address from the DHCP server. Take note: the ‘Spoofed PC’ acts as a regular client, even though the name indicated differently. This is because we’re reusing the topology from the previous blog.

PC1:

sudo ifconfig eth1 0.0.0.0
sudo dhclient eth1

cisco@server-1:~$ ifconfig eth1 | grep "inet addr"
 inet addr:192.168.1.2 Bcast:192.168.1.255 Mask:255.255.255.0

Spoofed PC:

sudo ifconfig eth1 0.0.0.0
sudo dhclient eth1

cisco@server-3:~$ ifconfig eth1 | grep "inet addr"
 inet addr:192.168.1.3 Bcast:192.168.1.255 Mask:255.255.255.0

PC2:

sudo ifconfig eth1 0.0.0.0
sudo dhclient eth1

cisco@server-2:~$ ifconfig eth1 | grep "inet addr"
 inet addr:192.168.2.2 Bcast:192.168.2.255 Mask:255.255.255.0

Now let’s first verify connectivity between ‘PC1’ <> ‘Spoofed PC’ and ‘PC1’ <> ‘PC2’.

cisco@server-1:~$ ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=0.895 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=1.34 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=1.18 ms
^C
--- 192.168.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.895/1.141/1.341/0.187 ms

cisco@server-1:~$ ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_seq=1 ttl=63 time=2.55 ms
64 bytes from 192.168.2.2: icmp_seq=2 ttl=63 time=2.43 ms
64 bytes from 192.168.2.2: icmp_seq=3 ttl=63 time=2.57 ms
^C
--- 192.168.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 2.435/2.520/2.572/0.073 ms

Since it is a ping, or more precise, an ICMP echo request and an ICMP echo reply, we know connectivity is working for all machines, so we only need to test this from PC1.

Now let’s configure the first part of Dynamic ARP Inspection and see what happens to this traffic. Based on what we’re going to test, only configuring Dynamic ARP Inspection on the switch ‘Left’ will suffice.

Left:

conf t
ip arp inspection vlan 100

That’s it for enabling and configuring the use of Dynamic ARP Inspection for VLAN 100. As you might notice, in comparison to DHCP Snooping, you don’t need to enable it explicitly in global config with a separate command. You combine this with the VLAN statement.

To properly configure IP ARP Inspection, you will need to trust the uplink from the ‘Left’ switch towards the ‘Router’, or else any ARP’ing for addresses outside of the LAN will be denied, which will result in non-LAN traffic being stopped altogether. This is of course because the default gateway of the PC’s (the ‘Router’) will not be in the DHCP Snooping table and therefore be denied. This results in the PC’s not aquiring the ARP address of the ‘Router’ and in result won’t be able to reach traffic outside of the LAN.

To illustrate what happens if we don’t trust the router facing interface, let’s see how our pings are holding up now:

cisco@server-1:~$ ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
From 192.168.1.2 icmp_seq=1 Destination Host Unreachable
From 192.168.1.2 icmp_seq=2 Destination Host Unreachable
From 192.168.1.2 icmp_seq=3 Destination Host Unreachable
^C
--- 192.168.2.2 ping statistics ---
4 packets transmitted, 0 received, +3 errors, 100% packet loss, time 3014ms

The follow output shows the log from our ‘Left’ switch:

*Mar 7 18:22:21.717: %SW_DAI-4-DHCP_SNOOPING_DENY: 1 Invalid ARPs (Res) on Gi0/2, vlan 100.([fa16.3e2c.0bbd/192.168.1.1/fa16.3e4f.2f9f/192.168.1.3/18:22:21 UTC Wed Mar 7 2018])
*Mar 7 18:22:33.723: %SW_DAI-4-DHCP_SNOOPING_DENY: 1 Invalid ARPs (Res) on Gi0/2, vlan 100.([fa16.3e2c.0bbd/192.168.1.1/fa16.3e4f.2f9f/192.168.1.3/18:22:33 UTC Wed Mar 7 2018])
*Mar 7 18:22:34.723: %SW_DAI-4-DHCP_SNOOPING_DENY: 1 Invalid ARPs (Res) on Gi0/2, vlan 100.([fa16.3e2c.0bbd/192.168.1.1/fa16.3e4f.2f9f/192.168.1.3/18:22:34 UTC Wed Mar 7 2018])
*Mar 7 18:22:35.733: %SW_DAI-4-DHCP_SNOOPING_DENY: 1 Invalid ARPs (Res) on Gi0/2, vlan 100.([fa16.3e2c.0bbd/192.168.1.1/fa16.3e4f.2f9f/192.168.1.3/18:22:35 UTC Wed Mar 7 2018])

Notice that our ‘PC1’ will also not have an ARP entry for ‘PC2’:

cisco@server-1:~$ arp -n | grep 192.168.2.2
192.168.2.2 (incomplete) eth1

But since Dynamic ARP Inspection is only blocking ARP packets, we can simply restore our traffic flow by configuring a static ARP entry on ‘PC1’, as follows:

sudo arp -s 192.168.2.2 fa:16:3e:2c:0b:bd

cisco@server-1:~$ arp -n | grep 192.168.2.2
192.168.2.2 ether fa:16:3e:2c:0b:bd CM eth1

And behold, we restored the traffic flow!

cisco@server-1:~$ ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_seq=1 ttl=63 time=2.57 ms
64 bytes from 192.168.2.2: icmp_seq=2 ttl=63 time=2.71 ms
64 bytes from 192.168.2.2: icmp_seq=3 ttl=63 time=2.30 ms
64 bytes from 192.168.2.2: icmp_seq=4 ttl=63 time=2.58 ms
^C
--- 192.168.2.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 2.309/2.548/2.719/0.148 ms

In this case I’m using the MAC address of the router, since the router knows the way out of the LAN. In a normal functioning situation, the MAC address of the router would also be in the ARP table of ‘PC1’. So, as demonstrated, Dynamic ARP Inspection can easily be circumvented.

Static IP addresses with Dynamic ARP Inspection

Take note that it is not possible to use static IP addresses when you a basic Dynamic ARP Inspection configuration. This is ofcourse because you have not requested an IP address and therefore are not added to the DHCP Snooping Binding Table, which results in your ARP packets being blocked by Dynamic ARP Inspection.

Not being able to use static IP addresses might be a major drawback if you’re thinking about implementing this in your network.

To avoid this issue if you are using static IP addresses in your network, is to define access-lists and tell DAI to use these access-lists to ignore inspection for the content of the access-lists.

To demonstrate this, I’ve set an static IP address on ‘PC1’:

cisco@server-1:~$ sudo ifconfig eth1 192.168.1.10 netmask 255.255.255.0

cisco@server-1:~$ ifconfig eth1 | grep "inet addr"
 inet addr:192.168.1.10 Bcast:192.168.1.255 Mask:255.255.255.0

The pings towards 192.168.2.2 are not working anymore now, and the ‘Left’ switch will log the attempts. Let’s first create an access-list, defining what traffic needs to be ignored.

Left:

conf t
arp access-list STATIC
 permit ip host 192.168.1.10 mac host fa16.3ea7.e993

This is not an ordinary access-list, but an ARP access-list. It works differently than a “normal” access-list by only being focussed on ARP traffic. We then have to tell DAI to use the ARP ACL with the following command:

ip arp inspection filter STATIC vlan 100

With the above rule, we have allowed a specific host with a combination of its IP and MAC address. The pings are once again working at this point.

You can also make the access-list a bit less restrictive in the following way:

conf t 
arp access-list STATIC 
 permit ip 192.168.1.0 0.0.0.255 mac any

This will allow all IP addresses from the subnet 192.168.1.0/24, regardless of which MAC address they are using.

If you’re in a situation where you have some devices that can only operate with a static IP address, using an ARP ACL is the way forward.

IP Source Guard

Onwards with IP Source Guard! The reason IP Source Guard and Dynamic ARP Inspection are often used within the same sentence, is because they are both technologies that make use of the DHCP Snooping Binding Table. They are, however, very different from one another.

Whereas Dynamic ARP Inspection will filter ARP traffic, IP Source Guard will (as the name implies) filter traffic based on its source IP address. You will want to implement this if you want to counter source IP spoofing. This is when a malicious host will pretend to be a legitimate host by using the source IP address of the legitimate host.

IP Source Guard will block traffic where the source IP address does not match the MAC address in the DHCP Snooping Binding Table. It works on layer 2 of the OSI model and will dynamically create Port Access Control Lists (PACL’s), which are fancy words for saying: It creates ACL’s which are bound at a port level (interfaces) rather than at a protocol level (i.e. for routing packets). If IP Source Guard is active on a interface which has not yet received an IP address from DHCP, all traffic is dropped.

Let’s configure it! The most basic implementation, assuming you have already configured DHCP Snooping is as follows:

Left:

conf t
interface GigabitEthernet0/1
 ip verify source

That’s it! Now it’s active for this interface. It will check the DHCP Snooping binding table for an entry, and based on that allow or block traffic. The following output verifies the operation:

Left#sh run int gi0/1
Building configuration...

Current configuration : 162 bytes
!
interface GigabitEthernet0/1
 switchport access vlan 100
 switchport mode access
 media-type rj45
 negotiation auto
 ip verify source
end

Left#sh ip dhcp snooping binding 
MacAddress         IpAddress       Lease(sec) Type          VLAN Interface
------------------ --------------- ---------- ------------- ---- --------------------
FA:16:3E:A7:E9:93  192.168.1.2     141        dhcp-snooping 100  GigabitEthernet0/1

Left#sh ip verify source 
Interface Filter-type Filter-mode IP-address      Mac-address       Vlan
--------- ----------- ----------- --------------- ----------------- ----
Gi0/1     ip          active      192.168.1.2                       100

We can see that it is active and running for 192.168.1.2 in VLAN 100. It got all that data from the DHCP Snooping Binding table.

I’ve configured the ‘Spoofed PC1’ with the same IP address as our legitimate host ‘PC1’. Before I spoofed the IP address on ‘Spoofed PC1’, connectivity from ‘PC1’ towards ‘PC2’ was working as it should. ARP entries on the router correlated to ‘PC1’ etcetera.

I configured the following on ‘Spoofed PC1’:

sudo ifconfig eth1 192.168.1.2 netmask 255.255.255.0 gateway 192.168.1.1

This will enable 192.168.1.2 on interface eth1. At that point, ‘PC1’ lost connection to ‘PC2’, even though the ARP entry on the router was still active. ‘Spoofed PC1’ basically took over the IP address.

Now watch what happens when we enable IP Source Guard on the interface facing ‘Spoofed PC1’:

Left#sh ip verify source 
Interface Filter-type Filter-mode IP-address      Mac-address       Vlan
--------- ----------- ----------- --------------- ----------------- ----
Gi0/1     ip          active      192.168.1.2                       100 
Gi1/0     ip          active      deny-all                          100

Traffic coming from Gi1/0 is now blocked, because there is no correlation between interface and IP address from the information in the DHCP Snooping Binding Table.

If need be, you can statically assign IP addresses that will circumvent the IP Source Guard feature. Configure the following in global config:

Left(config)#ip source binding FA16.3EA7.E993 vlan 100 192.168.1.2 interface Gi0/1

With the command ‘show ip source binding’, you can see the effect this has:

Left#show ip source binding 
MacAddress IpAddress Lease(sec) Type VLAN Interface
------------------ --------------- ---------- ------------- ---- --------------------
FA:16:3E:A7:E9:93 192.168.1.2 infinite static 100 GigabitEthernet0/1

Traffic for this combination of IP and MAC will be allowed from the moment you set the static entry.

Thanks for reading, happy configuring!