Tuesday, July 8, 2014

VTP v3

VTP v3 isn't technically a "new addition" to the CCIE lab, but code versions prohibited it from being used up until recently. I've been told IOL does in fact support VTP v3, so it should be considered a viable lab topic now.

So what's new in VTP v3?  In no particular order:

- Supports extended VLANs (1006 - 4094)
- Support for propagating Private VLANs
- Support for propagating Multiple Spanning Tree
- Support for flagging VLANs as RSPAN (disables MAC learning on the VLAN)
- Fixes the bane of VTP v1/2, the accidental-high-configuration-revision-wipes-out-your-network issue.
- VTP can now be turned off completely, as opposed to just transparent mode
- Support for hidden passwords

My lab is two 3560s running 15.0(2)SE6, and one 3550 running 12.x code. The 3560s support VTP v3, the 3550 I'm just using to show backwards compatibility to v2.

My switches are plugged in in a row:

SW1 3560 --> SW2 3560 --> SW3 3550

Let's enable v3 on SW1 and SW2:

SW1(config)#vtp version 3
Cannot set the version to 3 because domain name is not configured

You can't enable v3 without specifying a domain. Previous versions of VTP just inherited the domain name from its neighbors if you didn't specify one. This ties in some to the security measures, we don't necessarily want to participate in the neighbor's VTP process, so don't make assumptions.

SW1(config)#vtp domain CCIE
Changing VTP domain name from NULL to CCIE
*Mar  1 00:04:08.638: %SW_VLAN-6-VTP_DOMAIN_NAME_CHG: VTP domain name changed to CCIE.
SW1(config)#vtp version 3
*Mar  1 00:04:12.908: %SW_VLAN-6-OLD_CONFIG_FILE_READ: Old version 2 VLAN configuration file detected and read OK.  Version 3
    files will be written in the future.

I've done the same on SW2.

Let's try adding some VLANs.

SW1(config)#vtp mode server
Setting device to VTP Server mode for VLANS.
SW1(config)#vlan 100
VTP VLAN configuration not allowed when device is not the primary server for vlan database.

Let's stop here and talk about a huge problem with previous versions of VTP. As a network consultant, I always recommend - especially prior to version 3 - that customers use VTP mode transparent. The problem is that VTP devices - VTP clients included - can have their VLANs removed or changed while not connected to the mothership, and inadvertently end up with a higher configuration revision. When that switch is introduced, or reintroduced, to the greater network, the higher configuration revision "wins", and the rest of the network replicates that VLAN database, erasing their own VLANs. This can be so dramatic that the entire network can end up with just VLAN 1, and the entire layer 2 domain goes down. This is a very easy problem to create, and causes a dramatic outage.

VTPv3 can no longer create this issue.

VTP mode clients, and secondary servers cannot write the VLAN database. What's a secondary server? Well, it's any server that isn't the primary! (sorry, couldn't resist).

There can only be one primary server. The primary server is the only server allowed to write the VLAN database:

SW1#vtp primary vlan
This system is becoming primary server for feature vlan
No conflicting VTP3 devices found.
Do you want to continue? [confirm]
*Mar  1 00:30:19.564: %SW_VLAN-4-VTP_PRIMARY_SERVER_CHG: 0014.1ceb.f600 has become the primary server for the VLAN VTP feature

SW1 is now the only device that can make changes to the contiguous v3 PVST VLAN database. Note the command vtp primary vlan is in privilege exec mode and is not saved to the config - if you reboot you lose this privilege. This completely eliminates the possibility of have a plug-and-play way of accidentally overwriting another network's VTP database.

SW1#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
SW1(config)#vlan 100

SW2#show vlan | i 100
100  VLAN0100                         active
<output omitted for brevity>

I'd like to spend a moment looking at the primary server takeover process in a little more detail.
As I mentioned, only one server can be primary.

So if we do this from SW2:

SW2#vtp primary vlan
This system is becoming primary server for feature vlan
No conflicting VTP3 devices found.
Do you want to continue? [confirm]
*Mar  1 00:52:46.632: %SW_VLAN-4-VTP_PRIMARY_SERVER_CHG: 0014.1cec.0280 has become the primary server for the VLAN VTP feature

So I'm confused by "No conflicted VTP3 devices found." I'm not sure what a conflicting server would be if not the existing primary server, but my switches always produce this output, so maybe it's a version/platform issue.

Anyway, if you look at SW1:

*Mar  1 00:53:36.963: %SW_VLAN-4-VTP_PRIMARY_SERVER_CHG: 0014.1cec.0280 has become the primary server for the VLAN VTP feature

SW1#show vtp status
VTP Version capable             : 1 to 3
VTP version running             : 3
VTP Domain Name                 : CCIE
VTP Pruning Mode                : Disabled
VTP Traps Generation            : Disabled
Device ID                       : 0014.1ceb.f600

Feature VLAN:
VTP Operating Mode                : Server
Number of existing VLANs          : 6
Number of existing extended VLANs : 0
Maximum VLANs supported locally   : 1005
Configuration Revision            : 2
Primary ID                        : 0014.1cec.0280
Primary Description               : SW2
MD5 digest                        : 0x73 0x33 0x29 0x15 0x3B 0xA7 0x29 0x04
                                    0x74 0x34 0x70 0x4F 0x58 0x74 0xAF 0x5E

Feature MST:
VTP Operating Mode                : Transparent

Feature UNKNOWN:
VTP Operating Mode                : Transparent

Note the Device ID -  0014.1ceb.f600, and then the Primary ID of  Feature VLAN - 0014.1cec.0280 (SW2's ID). SW2 just stole it from SW1. 

It's hard to show in the blog, but the process of becoming primary actually takes a little bit. There's a quicker way to steal it, which "doesn't check for conflicting devices" (not that I can seem to find conflicting devices anyway):

SW1#vtp primary vlan force
This system is becoming primary server for feature vlan
*Mar  1 00:59:32.112: %SW_VLAN-4-VTP_PRIMARY_SERVER_CHG: 0014.1ceb.f600 has become the primary server for the VLAN VTP feature

While I'm on the topic, you can actually see your VTP neighbors now (provided they're running v3):

SW1#show vtp device
Retrieving information from the VTP domain. Waiting for 5 seconds.

VTP Feature  Conf Revision Primary Server Device ID      Device Description
------------ ---- -------- -------------- -------------- ----------------------
VLAN         No   2        0014.1ceb.f600 0014.1cec.0280 SW2

Let's try to add some high-number VLANs now.

On the primary:
SW1(config)#vlan 1006

On the secondary, verifying
SW2#show vlan | i 1006
1006 VLAN1006                         active
<output omitted for brevity>

Let's make a routed port on the secondary (an SVI would work as well):
SW2(config)#int fa0/1
SW2(config-if)#no switchport
SW2(config-if)#ip address

Adding another VLAN on the primary:
SW1(config)#vlan 1007

And verifying on the secondary:
*Mar  1 01:04:07.384: %PM-4-EXT_VLAN_INUSE: VLAN 1007 currently in use by FastEthernet0/1
*Mar  1 01:04:07.384: %SW_VLAN-4-VLAN_CREATE_FAIL: Failed to create VLANs 1007: VLAN(s) not available in Port Manager

Well that didn't work.

Whenever you create a routed interface (SVI or interface-based) on a Catalyst switch, it assigns a VLAN between the routed interface and the control plane (best I can tell, that's what's happening...). I've heard an argument that this behavior has to do with allocating the BIA MAC addresses to routed interfaces, but if you look around, there are some Catalyst switches that just assign the same MAC to every routed interface by default, yet they still all use separate VLAN numbers, so I'm not inclined to believe that.

Anyway, the default behavior on my 3560s is to allocate these VLANs from 1006 and counting upwards, so if 1006 is in use, 1007 will be grabbed, which is what we just saw.  

vlan internal allocation policy ascending

You've seen this command if you've used a 3K series switch before, because you can't turn it off.

SW1#sh run | i ascen
vlan internal allocation policy ascending
SW1#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
SW1(config)#no vlan internal allocation policy ascending
SW1(config)#do sh run | i ascend
vlan internal allocation policy ascending
SW1(config)#vlan internal allocation policy ?
  ascending  Allocate internal VLAN in ascending order
SW1(config)#vlan internal allocation policy ascending ?

SW1(config)#vlan internal allocation policy ascending

So, tough luck, they're ascending!  

Some higher-end platforms support descending, I don't have one sitting around to lab, but I'm told descending starts at 4094 and counts downward instead.

Back to reality on my 3560s...
SW2#show vlan internal usage

VLAN Usage
---- --------------------
1007 FastEthernet0/1

Shutting down Fa0/1 releases Vlan 1007, but we're now out-of-sync. I let this sit a while and it never caught up, so I'm guessing you're just out of luck until the primary server pushes another update.

SW2(config)#int fa0/1

SW1(config)#no vlan 1007
SW1(config)#vlan 1007

SW2(config-if)#do show vlan | i 1007
1007 VLAN1007                         active
<output omitted>

So we've covered the primary VTP server, exactly what is the difference between a secondary VTP server and a VTP client?

Well, on the 3560, not much.

According to the VTPv3 documentation:
"Client: A device using a local temporary storage space (for example, DRAM) to hold via VTP received information for runtime use. This information is used to update other devices, such as a device that is working as a server. Local configuration of devices in the client role is not possible. After booting, a client device issues a VTP message asking for the configuration of other VTP devices."

This implies that the VTP secondary server saves its database to flash and the client doesn't store it at all. And on the 3560? My VTP clients (and secondary servers) store the full VTP database and will load it up every time unless I manually delete it. So in practice, on this equipment, I can't actually find a difference between VTP secondary servers and VTP clients.

I'm told a best practice is to demote the primary vtp server when you're done making changes. The method to accomplish that is not particularly clear, but here you have it:

SW1(config)#vtp mode client
Setting device to VTP Client mode for VLANS.
SW1(config)#vtp mode server
Setting device to VTP Server mode for VLANS.

Now you're a secondary server.

Let's take a look at the new password features. The old, plain-text password still works the same way. The hidden ones, however:

SW1(config)#vtp password CANTSEEME hidden
Setting device VTP password

SW1#show vtp password
VTP Password: 80B0218C160CD951A38982EECCC22AD5

There's apparently no way to recover it, even snooping through the vlan.dat file, so if you need to add a switch without giving the password out:

SW2(config)#vtp password 80B0218C160CD951A38982EECCC22AD5 secret
Setting device VTP password

Of note, the password is required (the unencrypted one) in order to promote a server to primary:
SW1#vtp primary vlan
This system is becoming primary server for feature vlan
Enter VTP Password:

How about interoperability with previous versions?

Previous version switches will promote themselves from v1 to v2 if connected to a v3 device:

Prior to talking to SW2:

SW3#show vtp status
VTP Version                     : running VTP1 (VTP2 capable)
Configuration Revision          : 0
Maximum VLANs supported locally : 1005
Number of existing VLANs        : 5
VTP Operating Mode              : Server
VTP Domain Name                 :
VTP Pruning Mode                : Disabled
VTP V2 Mode                     : Disabled
VTP Traps Generation            : Disabled
MD5 digest                      : 0x57 0xCD 0x40 0x65 0x63 0x59 0x47 0xBD
Configuration last modified by at 0-0-00 00:00:00
Local updater ID is (no valid interface found)

After talking to SW2:
SW3#show vtp status
VTP Version                     : running VTP2
Configuration Revision          : 1
Maximum VLANs supported locally : 1005
Number of existing VLANs        : 7
VTP Operating Mode              : Server
VTP Domain Name                 : CCIE
VTP Pruning Mode                : Disabled
VTP V2 Mode                     : Enabled
VTP Traps Generation            : Disabled
MD5 digest                      : 0x60 0xEC 0xC1 0xEF 0xEF 0xE3 0x24 0xB6
Configuration last modified by at 3-1-93 00:00:58
Local updater ID is (no valid interface found)

Note the automatic V2 change. I actually had a heck of a time getting this working when I re-labbed it for this document, and that's because I had left a hidden password on SW1 and SW2. VTPv1/2 aren't going to speak hidden password, turn that off first!

The important thing to grasp about the v2 compatibility is this must be a one-way path: The v3 network needs to make the database changes. It's best to keep the entire v2 domain as clients. If you make changes in v2, the v3 devices will not accept the changes, but the v2 domain will up its configuration revision number. Then when v3 pushes a legitimate update, the v2 domain will reject it because it will by definition be lower than that of v2.  You end up with a segmented VTP domain, and a royal mess in the v2 network:

*Mar  1 06:17:23.646: %SW_VLAN-4-VTP_USER_NOTIFICATION: VTP protocol user notification: MD5 digest checksum mismatch on receipt of equal revision summary on trunk: Fa0/21

*Mar  1 06:17:23.650: %SW_VLAN-4-VTP_USER_NOTIFICATION: VTP protocol user notification: MD5 digest checksum mismatch on  receipt of equal revision summary on trunk: Fa0/22

Disabling VTP: Why?

Most of us have been happy to use transparent for years. The big difference with disabling VTP as opposed to using transparent mode is that the switch won't even pass VTP messages in "off" mode, it deliberately filters them. The benefit would be for a network administrative boundary, like connecting trunks between two carriers.

SW2(config)#vtp mode off
Setting device to VTP Off mode for VLANS.

Per interface:
SW2(config)#int fa0/21
SW2(config-if)#no vtp

The Private VLAN support sounds daunting, but it really does a very simple task. All it does is carry the VLAN associations, it's not assigning interface or trunk configs anywhere.

SW1(config-vlan)#vlan 601
SW1(config-vlan)#  private-vlan isolated
SW1(config-vlan)#vlan 600
SW1(config-vlan)#  private-vlan primary
SW1(config-vlan)#  private-vlan association 601

It doesn't matter where I trunk this, or what ports are applied in what fashion. This is basically all we're replicating:

SW1#show vlan private-vlan

Primary Secondary Type              Ports
------- --------- ----------------- ------------------------------------------
600     601       isolated

SW2#show vlan private-vlan

Primary Secondary Type              Ports
------- --------- ----------------- ------------------------------------------
600     601       isolated          Fa0/6

Of note, and this will make more sense as we move into MST, the private-vlan feature is an add-on to the "VLAN" feature, which you may have noticed inside the show vtp status output:

SW1#show vtp status
VTP Version capable             : 1 to 3
VTP version running             : 3
VTP Domain Name                 : CCIE
VTP Pruning Mode                : Disabled
VTP Traps Generation            : Disabled
Device ID                       : 0014.1ceb.f600

Feature VLAN:
VTP Operating Mode                : Primary Server
Number of existing VLANs          : 9
Number of existing extended VLANs : 2
Maximum VLANs supported locally   : 1005
Configuration Revision            : 3
Primary ID                        : 0014.1ceb.f600
Primary Description               : SW1
MD5 digest                        : 0xBE 0x75 0xED 0x56 0xCB 0xAF 0xF3 0xF6
                                    0x59 0x8D 0x91 0x6C 0x60 0x28 0x55 0xEB

Feature MST:
VTP Operating Mode                : Transparent

Feature UNKNOWN:
VTP Operating Mode                : Transparent

Now let's talk about those last two, Feature MST and Feature UNKNOWN.

The MST config is pretty cool. If you choose to run MST, the big drag is the manual configuration updates, and this fixes all of them.

Just as with feature VLAN, we need to make a primary server for MST. Note, this does not have to be the same switch as the feature VLAN. In fact, I'm leaving SW1 the primary for feature VLAN, and making SW2 the primary for feature MST:

SW2(config)#vtp mode server mst
Setting device to VTP Server mode for MST.

SW2#vtp primary mst force
This system is becoming primary server for feature  mst
*Mar  1 00:17:26.453: %SW_VLAN-4-VTP_PRIMARY_SERVER_CHG: 0014.1cec.0280 has become the primary server for the MST VTP feature

Feature VLAN:
VTP Operating Mode                : Server
Number of existing VLANs          : 9
Number of existing extended VLANs : 2
Maximum VLANs supported locally   : 1005
Configuration Revision            : 3
Primary ID                        : 0014.1ceb.f600
Primary Description               : SW1
MD5 digest                        : 0xBE 0x75 0xED 0x56 0xCB 0xAF 0xF3 0xF6
                                    0x59 0x8D 0x91 0x6C 0x60 0x28 0x55 0xEB

Feature MST:
VTP Operating Mode                : Primary Server
Configuration Revision            : 1
Primary ID                        : 0014.1cec.0280
Primary Description               : SW2
<output omitted for brevity>

SW1(config)#vtp mode client mst
Setting device to VTP Client mode for MST.

SW2(config)#spanning-tree mst config
SW2(config-mst)#instance 1 vlan 1-100
SW2(config-mst)#instance 2 vlan 101-200
SW2(config-mst)#instance 3 vlan 201-300
SW2(config-mst)#name region1
SW2(config-mst)#revision 1

SW2(config)#spanning-tree mode mst

SW2#show spanning-tree mst config
Name      [region1]
Revision  1     Instances configured 4

Instance  Vlans mapped
--------  ---------------------------------------------------------------------
0         301-4094
1         1-100
2         101-200
3         201-300

Enable MST on SW1:

SW1(config)#spanning-tree mode mst

And we'll see it has the configuration already:

SW1#show span mst config
Name      [region1]
Revision  1     Instances configured 4

Instance  Vlans mapped
--------  ---------------------------------------------------------------------
0         301-4094
1         1-100
2         101-200
3         201-300

Another nifty thing is that it actually updates the running config to match:

SW1#show run | s spanning-tree mst
spanning-tree mst configuration
 name region1
 revision 1
 instance 1 vlan 1-100
 instance 2 vlan 101-200
 instance 3 vlan 201-300

Reverting to PVST for simplicity --
SW1(config)#spanning-tree mode rapid-pvst
SW2(config)#spanning-tree mode rapid-pvst

The Remote SPAN flag is quite simple:

SW1(config)#vlan 150

SW2#show vlan remote-span


The purpose here is to tell all the switches in the forwarding path of the remote SPAN not to learn MAC addresses on that VLAN.

Feature UNKNOWN is actually kinda cool too, although I have no way of demonstrating it. VTPv3 is designed to carry different types of databases, so that it can be adapted to other replication tasks in the future. So what's an earlier VTPv3 IOS to do with these new formats it doesn't understand? Forward them or drop them?

SW1(config)#vtp mode off unknown
Setting device to VTP Off mode for unknown instances.
SW1(config)#vtp mode transparent unknown
Setting device to VTP Transparent mode for unknown instances.

You can set them only to off or transparent. Clearly can't be a VTP server for a format you don't understand.  off works the same way explained above, drop the traffic; transparent forwards without processing.

And lastly, where's this at in the DocCD?:

Switches -> 
3850 -> 
Catalyst 3850-12S-E Switch -> 
Configuration Guides -> 
VLAN Configuration Guide, Cisco IOS XE Release 3SE (Catalyst 3850 Switches) ->
Configuring VTP

v3 is just a section in the larger VTP configuration guide, but everything you need should be there.



Saturday, July 5, 2014

IPv6 First Hop Security

IPv6 First Hop Security is a new topic for CCIE v5. It's important to note that at the time of this writing (June/July 2014), IPv6 FH Security is not supported in IOL, so this cannot be on the CLI-based parts of the lab yet, but it can be in diagnostics or the written.

The biggest barrier to understanding IPv6 FH Security is understanding the whole first hop process to begin with.  IPv6 changes this dramatically from the IPv4 model. First, we will examine IPv6 First Hop and determine where the security problems are.

First, let's answer a simple question: How do I receive a routable IPv6 address?

ICMP communicates nearly everything regarding IPv6 addressing. It's used for finding a router, building an address (typically), making sure it's unique, finding a DHCP server if necessary, locating other hosts, assigning a default route, etc.

That said, IPv6 has a major chicken-or-the-egg problem. You can't run ICMP effectively without having an IPv6 address already, but how can you have an address before... you have an address?

Enter link-local addressing. Link local addresses all exist within FE80::/10. Typically, and true on Cisco devices, this address is built as FE80::<address based on MAC address>. It can alternatively (as is done in modern Windows OS) be built from FE80::<random address>. The specific format of the address isn't necessary for my explanation, so I will consider that out-of-scope for this document, and it can be found in hundreds of other places.

Since my blog is geared towards the CCIE, and we know we'll be using all Cisco kit, we can assume that the MAC address method is what we'll be using.

So we've built our interface a "link local" address of FE80::FA66:F2FF:FEDE:FF1. This link-local is non-routable, and can only be reached on the local segment. We must immediately run a process called DAD (Duplicate Address Detection) to ensure that we're the only person using this address. When we built the link local address, we also joined a multicast group called a "solicted node multicast", which is a multicast address that's unique for our host. DAD sends a multicast to this solicited node, and if anyone else responds (they shouldn't, if the address is unique), then we drop the address and don't use it.

Assuming the address is unique, our next goal is to come up with a routable address in addition to our link local. Now that we have a link-local address, we can send out a Router Solicitation (RS) and ask for the global prefix we should be on.  Let's say our router's IPv6 address is 2001:100::1/64. The router will fire back a Router Advertisement (RA) to our host that it's prefix is 2001:100::/64. We'll use the same process we used for link-local earlier to assign our globally routable address. Using the same example above, that address would be 2001:100::FA66:F2FF:FEDE:FF1.

This process is called StateLess Address AutoConfiguration, or SLAAC. It's important to note that the router must send out an RA with a /64 length, or this process doesn't work.

Perhaps more important to our examples, the RA we got the prefix from also hands out the router's IPv6 link local address, which we can optionally use as our default gateway. Just to point out again: this is not a global address. SLAAC's default routing is always done with link-local addresses.

Let's look at the rather minor config for this thus far:

R1 ("router"):
ipv6 unicast-routing

interface Gigabit0/1
  ipv6 address 2001:100::1/64

R2 ("host"):
ipv6 unicast-routing ! May or may not be necessary for a "host", platform-dependent.

interface Gigabit0/1
  ipv6 address autoconfig default

Omitting the "default" above would prevent it from installing R1 as a default route.

R2(config-if)#do sh ipv6 int br | s GigabitEthernet0/1
GigabitEthernet0/1         [up/up]

R2#sh ipv6 route ::/0
Routing entry for ::/0
  Known via "static", distance 2, metric 0
  Route count is 1/1, share count 0
  Routing paths:
    FE80::F2F7:55FF:FE8D:96A2, GigabitEthernet0/1
      Last updated 00:00:10 ago

FE80::F2F7:55FF:FE8D:96A2 is R1's link local address.

Cisco routers all announce themselves as viable gateways (imagine that), so if you have a "host" router - perhaps a voice gateway or whatnot - you need to tell it not to send router advertisements:

R2(config)#int gig0/1
R2(config-if)#ipv6 nd ra suppress all

The suppress keyword indicates not to send periodic RAs, suppress all means don't respond to RSes.

An optional feature that's not on all platforms:
R2(config-if)#ipv6 address autoconfig prefix

That will make the router insert routes for any other routes on the same segment. So if your neighbor had a second IPv6 address of 13::1, we'd insert a route to it as well.

So that gets us a link-local address, global unicast address, and default gateway. What about DNS?

Up until very recently, the only real option was to run a stateless DHCPv6 server. Stateless because in this scenario, the DHCPv6 server doesn't actually keep track of anything, it just hands out options: DNS, Call Manager info, etc.

R1(config)#ipv6 dhcp pool DHCP-POOL
R1(config-dhcpv6)#dns-server 4::4
R1(config-dhcpv6)#dns-server 8::8
R1(config-dhcpv6)#domain-name ABC.COM
R1(config-dhcpv6)#int gig0/1
R1(config-if)#ipv6 dhcp server DHCP-POOL
R1(config-if)#ipv6 nd other-config-flag

The process on R2 is automatic, but on R1 we create the pool, which is reasonably obvious config, apply it to the interface, and then set the O-flag. This tells clients via RA that it should query for a DHCP server for more information. The DHCP server and the device sending the RA do not need to be the same device.

Great! We've got our addresses, default gateway, and DNS. Before we move on to neighbor discovery, let's look at stateful DHCP.

You've got a couple options here.

Either way we need more info in our DHCP server:

R1(config)#ipv6 dhcp pool DHCP-POOL
R1(config-dhcpv6)#address prefix 2001:100::/64

We'll still get our default gateway through RAs, but we can at least track the addresses that our hosts are using.

Our first option is to just recommend to our host that it use stateful DHCP.

R1(config-if)#no ipv6 nd other-config-flag
R1(config-if)#ipv6 nd managed-config-flag

The M-Flag (Managed Flag) is a suggestion to the client that it should use the DHCP server for its host address instead of SLAAC.

Since we're using a Cisco client, I need to stop here and advise that I've never been able to get the client to recognize the M-flag. It could be my IOS, I haven't investigated it that much.

Since that's a bust, let's look at the other method.

R2(config-if)#int gig0/1
R2(config-if)#ipv6 address autoconfig default-route ! still need this for the default gateway
R2(config-if)#ipv6 address dhcp
R2(config-if)#ipv6 enable

I'll explain ipv6 enable momentarily.

R2(config-if)#do sh ipv6 int br | s GigabitEthernet0/1
GigabitEthernet0/1         [up/up]

We got our address. (Side-note: if you have ipv6 address dhcp on an interface and not ipv6 address autoconfig default-route, you won't get a connected route to the local subnet - you get a totally unusable /128 host address and that's it. The workaround is to disable ipv6 unicast-routing, then you'll get the connected route)

R1#sh ipv6 dhcp binding
Client: FE80::FA66:F2FF:FEDE:FF1
  DUID: 00030001F866F2DE0FF0
  Username : unassigned
  IA NA: IA ID 0x00040001, T1 43200, T2 69120
    Address: 2001:100::2C11:9212:8690:D8FE
            preferred lifetime 86400, valid lifetime 172800
            expires at Jul 02 2014 01:29 AM (172654 seconds)

and R1 knows about us - it's stateful.

There's also a new option defined by RFC 6106 that completely eliminates the need for DHCPv6 in relation to DNS. Unfortunately, at the time of this writing, you need bleeding-edge IOS-XE in order to use it. In fact, the hardware lab I'm using (which will be explained later) for this doesn't even support it - I have to turn to CSR1000v:

Router(config-if)#ipv6 nd ra dns server ?
  X:X:X:X::X  IPv6 address

This passes DNS servers as an RA option. I have labbed this previously, it does appear to work from what I can see in Wireshark.

I promised an explanation to ipv6 enable. I bet you've seen this command before, followed by a statically configured address. You actually need this very infrequently. With the exception of the DHCP example I showed above, the only time you need ipv6 enable is if you want a eui-64 derived link-local address without a global unicast address. If you're statically assigning an IPv6 global unicast, or you're getting one via SLAAC, you don't need this command - so cut it out! :)

Of course your final option is a statically assigned address:

ipv6 address 1::1/64 

Link locals can also be statically assigned:

ipv6 address FE80::1 link-local

Now that we're past "how do we get an address", let's move on to the other big topic of "how do we find our neighbors". As you're probably already aware, IPv6 does not use ARP, instead it uses a Neighbor Solicitation (NS) and Neighbor Advertisement (NA) ICMPv6 messages. This builds the neighbor table instead of the ARP cache.

NS and NA map pretty directly in functionality to ARP request and ARP reply in IPv4. I'm not going to go over them in detail, just understanding their function is sufficient for now.

If R2 has just gotten it's address and wants to find R3, it would send out an NS.
I've reset R2 to SLAAC and setup R3 for SLAAC as well.
R2: 2001:100::FA66:F2FF:FEDE:FF1
R3: 2001:100::C67D:4FFF:FEF9:5340

R2#ping 2001:100::C67D:4FFF:FEF9:5340

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2001:100::C67D:4FFF:FEF9:5340, timeout is 2 seconds:
Success rate is 100 percent (5/5), round-trip min/avg/max = 0/3/16 ms

and some filtered output from debug ipv6 icmp:

ICMPv6: Sent N-Solicit, Src=2001:100::FA66:F2FF:FEDE:FF1, Dst=FF02::1:FFF9:5340
ICMPv6: Received N-Advert, Src=2001:100::C67D:4FFF:FEF9:5340, Dst=2001:100::FA66:F2FF:FEDE:FF1

There's our NS going out, and NA coming in. It's important to note that the NS also provided R3 with R2's layer 2 address, so there's no need for the reverse process to happen.

R2#sh ipv6 neighbor
IPv6 Address                              Age Link-layer Addr State Interface
2001:100::C67D:4FFF:FEF9:5340               0 c47d.4ff9.5340  REACH Gi0/1

R3#sh ipv6 neighbor
IPv6 Address                              Age Link-layer Addr State Interface
2001:100::FA66:F2FF:FEDE:FF1                0 f866.f2de.0ff1  REACH Gi0/0

As you can see, R2 is aware of R3's addresses and vice-versa.

Before I move on to pointing out the (somewhat obvious) security problems with this whole process, I'd like to pause and look at a few other features that I felt were of important note, but didn't fit into the explanation thus far.

If you have multiple routers on a segment and want to use one or more for failover, you can specify how important their advertisements are:

ipv6 nd router-preference med !default (setting this doesn't show in config)
ipv6 nd router-preference low !depreffed
ipv6 nd router-preference high !preffed

Although, I imagine most of us would just use HSRP.

Also, if you set the RA lifetime to 0, hosts won't use it as a default gateway, but it can still be used for SLAAC: ipv6 nd ra lifetime 0

I've previously run production dual-stack IPv6 at home. It was an interesting experiment, but the overwhelming lesson I learned from it is that most content providers are using separate pipes for their IPv6 traffic, and instead of being wide open and unused liked I'd hoped, they were miserably oversubscribed, so when my Windows installation naturally preferred a IPv6 DNS resolution when both IPv6 and IPv4 were available for the same site (Here's looking at you, Youtube!) all I got was slower web traffic. I still have the v6 service on my cablemodem, but I disabled it on the router.

One of the more interesting things I learned from it was the sudden realization of what the heck I do without NAT.  Almost all home networks are setup like so on IPv4:

[provider] ---- outside /30 ----> [home router] (NAT) --- private IP range ---> hosts

So if you think about this for a minute... in order to duplicate this on IPv6, which doesn't typically use NAT, you need a routable IPv6 address block on the outside of your home router, and a routable IPv6 address block on the inside of your router too. And since home ISPs don't exactly assign you two blocks of static addresses typically, you're also going to need a way to do this with DHCP or SLAAC.

There's a really clever fix for this in IPv6. It's called prefix delegation. The idea is that our provider will delegate an IPv6 block to our router, which will then in turn function as the DHCP server for that block.

Let's look at some sample config.

ISP router:
ipv6 local pool dhcpv6-pool1 2001:DB8:1200::/40 48

ipv6 dhcp pool test
 prefix-delegation pool dhcpv6-pool1 lifetime 1800 600
 dns-server 4::4
 dns-server 8::8 
 domain-name isp.net

interface GigabitEthernet1/0
 ipv6 address 12::1/64
 ipv6 nd other-config-flag
 ipv6 dhcp server test 

Here we've told the ISP router that it should delegate /48 blocks from a larger /40 block to every DHCP server that asks. 

Home router:
ipv6 dhcp pool MY-DNS  ! we'll use our DNS servers
 dns-server 44::44
 dns-server 88::88
 domain-name foo.com

interface GigabitEthernet1/0
 ipv6 address autoconfig default  ! we want a SLAAC address
 ipv6 dhcp client pd FOO  ! we'll collect prefix delegation for the next interface

interface GigabitEthernet2/0
 ipv6 address FOO ::/64 eui-64  ! we'll use a /64 out of whatever we got above
 ipv6 nd other-config-flag
 ipv6 dhcp server MY-DNS

So, lots going on on the home router.  First, we're going to assign our outside interface - Gig1/0 - an address via SLAAC.  This gives us the IPv6 equivalent of my "outside /30"  from my ASCII diagram above, albeit it on a much larger block than a IPv4 /30 :).  Next, we're going to create a prefix delegation named FOO, and pick up a prefix from our ISP server, which we already know will be a /48 from my explanation above. We'll then go to our inside interface, Gig2/0, and assign ourselves an EUI-64 address from the prefix delegation FOO. We'll of course be sending RAs, so our internal hosts will also use SLAAC to get an address on the inside, delegated subnet.

Our end device/host would have a simple config, just running SLAAC and being a stateless DHCP client. In IOS it'd just be:

interface GigabitEthernet1/0
 ipv6 address autoconfig default

Back on the ISP server, you get reverse route injection in the form of static IPv6 routes pointing to whomever you delegated the prefix to. These can be redistributed into your IGP or BGP.

Now let's look at what security faults all these features potentially have.

There's some obvious "for like" issues that IPv4 had, that carried over to IPv6:

- An attacker could pose as the stateful DHCP server, handing out either bad information for denial of service (DOS), or handing out an attacker's IPv6 address for a man-in-the-middle attack (MiM).
- An attacker could pose as another host. Let's say Host1 is trying to reach Host2, and Host3 is an attacker.  Host1 could send out a NS for Host2, and Host3 could send an NA masquerading as Host2, and Host1 may accept it as true if timed correctly. A similar process could be done from Host2 -> Host1, with Host3 - inserting itself there as well, and effectively inserting itself into the entire conversation - a MiM attack - with Host1 and Host2 none the wiser.

This is where the similarities with IPv4 end. Here are some of the new security concerns:

- A router advertisement could be faked, allowing a host to insert itself into a conversation, for a MiM attack.
- A router could advertise, from one router to another, a prefix that shouldn't be on the local link. This connected route would be seen as closer than a theoretical downstream router that's legitimately advertising the same prefix.
- An attacker could respond to every DAD request from a host, or even from the entire segment, effectively preventing hosts from using their legitimately unique IPv6 addresses, creating a DOS.
- An outside host - not one on your local segment - could send traffic in rapid-fire towards a large swath of your available IPv6 space. Many IPv4 segments were either behind NAT or simply weren't all that big, but IPv6 space is really, really large - a /64 - the common LAN segment size - is 18 quintillion addresses. No last-hop router has enough memory to send NS requests for 18 quintillion addresses without running out of memory or CPU, creating a DOS by worst-case crashing the router, best-case busying the CPU out to the point where new requests aren't serviced.

For me, the most confusing thing about IPv6 FH Security was that there are, as I understand it, an "old" way of doing things, and a "new" way of doing things. These overlap a lot, and there's no real discussion of this in the documentation, so figuring out when to use what was confusing.

We're going to start with the "old" way, which is reasonably well-documented and well-blogged on the Internet, but not particularly thorough, and then move on to the "new" way, which seems more complete.

First let's see about tackling invalid RAs.

The simplest, non-automatic way to prevent invalid RAs is to write an PACL for them:

ipv6 access-list IPv6
 deny icmp any any routeradvertisement
 permit ipv6 any any

and simply apply it to any device that shouldn't be sending RAs.

There's a gotcha here, in that there is a well-known exploit by sending fragmented packets. I'm not going to go into this in detail as hundreds have blogged about this already. The workaround is to add one more deny to the PACL:

deny ipv6 any any undeterminedtransport

This will make the ACL drop any IPv6 traffic where the router is unable to determine the transport type (no layer 4 information). Some say this makes for better security than the actual RA Guard feature, but debates like that are out of scope for the CCIE, so I'll leave that to others.

This feature is simple and well-documented enough that I'm not going to lab it.

Moving on to features we will be labbing, let's look at RA Guard. It's the automated, more granular version of what we just did with the PACL.

Here's the diagram we'll work off of for the remainder of the article.

As I have in many other blogs, I use GNS3 for diagramming, but I'm using physical gear for the lab. Unfortunately, IPv6 FH Security requires a bleeding-edge IOS or IOS-XE layer 2 device, so labbing it is not so easy. I was lucky enough that some friends were able to lend me a 4948-E running 15.2(1). So, I am running entirely physical gear in this lab, despite what's implied by the diagram above.

This is also good and bad, because I'm using a remote lab, I can't recable on the fly. So there are a few scenarios that I didn't lab out as thoroughly as I would've done normally, but the knowledge gained here should still be more than sufficient for what may appear on the lab.

R1 will represent our valid router.
R2 will (usually) represent our valid host.
R3 will represent our attacker.

The most common way to set this up is to basically un-trust all ports that shouldn't have a router on them, and trust all the ones that should. This is accomplished with this config:

ipv6 nd raguard policy MY_ROUTERS
 device-role router

ipv6 nd raguard policy MY_HOSTS
 device-role host  ! DEFAULT

ipv6 snooping logging packet drop  ! NEEDED FOR LOGGING

vlan configuration 123 ! all our hosts are on vlan 123
  ipv6 nd raguard attach-policy MY_HOSTS

int gig1/1
  ipv6 nd raguard attach-policy MY_ROUTERS

I know the first thing I thought when I saw this was "what the heck is 'vlan configuration XYZ'"?
I always did find it a bit strange back on the Catalyst 3560, when I learned it for CCIE v4, that QoS config would be put on an SVI even though the SVI sometimes had no IP address on it - you'd just create it for the QoS config. I guess someone at Cisco thought the same way; so now there's a specific configuration section for VLANs.

So in short what we accomplished above was to configure every port in vlan 123 to assume a "host" was attached to it - a host should never be sending RAs. We then overrode that configuration on gig1/1 and told the switch to expect a router there - routers are, of course, OK to hear RAs from. Interface configuration always overrides vlan configuration, and that's true for the rest of the features we'll review in this article, so I'm going to assume that's understood from here on in.

We enabled logging via ipv6 snooping logging packet drop, which will actually turn on the majority of the logging we need for this article. There is one additional command we'll cover later.

I instructed R2 and R3 not to send RAs, let's see what output we get if I attempt to enable them on R3.

R3-ATTACKER(config-if)#ipv6 nd router-preference high ! Let's attempt to become the preferred router
R3-ATTACKER(config-if)#no ipv6 nd ra suppress all

*Jul  3 18:15:58.734: %SISF-4-PAK_DROP: Message dropped A=FE80::C67D:4FFF:FEF9:5340 G=- V=123 I=Gi1/3 P=NDP::RA Reason=Message unauthorized on port

That's the extreme basics, and I did it in long-hand so to speak, here's the shorter way to accomplish the same thing with defaults:

ipv6 nd raguard policy MY_ROUTERS
 device-role router

vlan configuration 123
  ipv6 nd raguard

int gig1/1
  ipv6 nd raguard attach-policy MY_ROUTERS

This is true of just about every filtering command for IPv6 FH Security, if you use just the basic command with no policy, i.e. ipv6 nd raguard, you get the default untrusted configuration. Here, we just said don't trust any ports' RAs except Gig1/1.

Filtering can also be done per-VLAN: 

interface GigabitEthernet1/1
 switchport trunk allowed vlan 122,123
 switchport mode trunk
 ipv6 nd raguard attach-policy MY_ROUTERS vlan 123
 ipv6 nd raguard vlan 122

This hypothetical config would trust RAs on vlan 123, but not vlan 122.

There are many other options for RA Guard:

ipv6 nd raguard policy SAMPLE
 device-role router
On the 4948, I have four options here: router, host, switch, and monitor.
Router and Host we already covered. Switch, I don't understand, and I can't find any documentation explaining what it does. Suffice to say that out-of-the-box it doesn't allow RAs, so if you set it with no other options, you get something similar to "Host". Monitor I found some vague definitions on, but it too has a similar outcome as switch: No RAs.
 other-config-flag on
 Require the other-config-flat to be set in the RA or the policy will not pass the traffic.
Permits RAs -- I can't find any other benefits to it for RA Guard, although the other frameworks (name ND Inspection) preference ports that have trusted-port enabled if they have a conflict.
 router-preference maximum
Sets the highest allowed preference on the port. If you want to enforce a backup router to being "low" or "medium" preference, this will drop the packet if anything higher is advertised.
 hop-limit minimum
 hop-limit maximum 
RAs advertise what hosts should use as a TTL. You can use these two functions to control what the minimum and maximum advertised TTLs should be. Note, if you want to test this with all IOS devices, IOS can only send one of two options for this field - the default, which is 64 (this is NOT noted on the CLI anywhere, I figured it out the hard way) - or "unspecified", which basically means "use whatever you want".
If you want to match a specific size, such as 90, you would set minimum and maximum to the same value.
 managed-config-flag on ! Ensures the managed config flag ("please get your IPv6 address statefully from DHCPv6") is on, if not, drop the packet.
 match ipv6 access-list <ACL>
This will match the link local address that's advertising the RA. If no match on the access-list, drop the packet. Formatting is as such: permit ipv6 host FE80::F2F7:55FF:FE8D:96A1 any
 match ra prefix-list <prefix list>
This will match the prefix being advertised in the RA. If no match, drop the packet.

Covering each of these options would make the article drag on and on, so I'm going to give one large configuration and comment on it:

ipv6 access-list LL-EXAMPLE
  Permit ipv6 host FE80::F2F7:55FF:FE8D:96A1 any

ipv6 prefix-list PREFIX-EXAMPLE permit 2001:100::/64

ipv6 nd raguard policy SAMPLE
 device-role router
 other-config-flag on
 router-preference maximum medium
 hop-limit minimum 64
 hop-limit maximum 64
 match ipv6 access-list LL-EXAMPLE
 match ra prefix-list PREFIX-EXAMPLE

int gig1/1
  ipv6 nd raguard attach-policy SAMPLE

This sample would allow RAs, require the other-config-flag to be enabled, not allow a router preference higher than medium, ensure a strict TTL advertisement of 64, require the RA to be sourced from FE80::F2F7:55FF:FE8D:96A1, and only permit it to advertise 2001:100::/64.

DHCP Guard is a tad simpler.

We'll setup R1 as a stateful DHCP server, R2 as a DHCP client, and R3 as a malicious stateful DHCP server. I've removed all the RA Guard configuration to decrease the example complexity.

R1-ROUTER(config)#ipv6 dhcp pool TEST-POOL
R1-ROUTER(config-dhcpv6)#address prefix 2001:100::/64
R1-ROUTER(config-dhcpv6)#dns-server 4::4
R1-ROUTER(config-dhcpv6)#dns-server 8::8
R1-ROUTER(config-dhcpv6)#int gig0/0
R1-ROUTER(config-if)#ipv6 dhcp server TEST-POOL

R3-ATTACKER(config-if)#ipv6 dhcp pool TEST-POOL
R3-ATTACKER(config-dhcpv6)#address prefix 2001:101::/64
R3-ATTACKER(config-dhcpv6)#dns-server 2001:101::BAD
R3-ATTACKER(config-dhcpv6)#int gig0/0
R3-ATTACKER(config-if)#ipv6 address 2001:101::BAD/64
R3-ATTACKER(config-if)#ipv6 dhcp server TEST-POOL

and on our switch:

ipv6 prefix-list GOOD-PREFIX seq 5 permit 2001:100::/64 le 128 
ipv6 access-list LL
 permit ipv6 host FE80::F2F7:55FF:FE8D:96A2 any

ipv6 dhcp guard policy TRUSTED
 device-role server
 match server access-list LL
 match reply prefix-list GOOD-PREFIX

vlan configuration 123
 ipv6 dhcp guard attach-policy TRUSTED

I'm going to treat this a bit differently - I'm going to trust all ports, but require them to match a specific link local source and address range.

R2-HOST(config-if)#no ipv6 address autoconfig
R2-HOST(config-if)#ipv6 enable
R2-HOST(config-if)#ipv6 address dhcp

*Jul  3 20:39:34.985: %SISF-4-PAK_DROP: Message dropped A=FE80::C67D:4FFF:FEF9:5340 G=2001:101::68:6AF1:6FC2:5983 V=123 I=Gi1/3 P=DHCPv6::ADV Reason=The source address in the DHCPv6 ADVERTISE packet is not authorized by the DHCP Guard policy

R2-HOST#sh ipv6 int br | s GigabitEthernet0/0
GigabitEthernet0/0         [up/up]

We get the correct address from the correct server.

Now moving on to a-la-carte Neighbor Discovery Inspection.

The first thing to understand about Neighbor Discovery is that it's a control plane feature only - it doesn't inspect actual data traffic, it is only looking at ND ICMP packets, and that's it - so if your users spoof their source address in an actual traffic flow, this doesn't protect against it.

IPv6 ND Inspection builds a table based on NS/NA messages. It then enforces the table. The funny thing about it is, with ND/NA, basically the first one it hears becomes the trusted one.

WS-C4948E(config)#vlan configuration 123
WS-C4948E(config-vlan-config)#ipv6 nd inspection

I really expected DHCP Guard to also populate this table, but the a-la-carte version of ND Inspection and DHCP Guard don't appear to talk to one another, as best I was able to tell. The unified (ipv6 snooping) feature, which we will see next, does populate the neighbor bindings from DHCP, so don't be confused as to why you see DHCP as a population feature in the screen shot.

So let's spoof R2 (Gig1/2)'s link local from R3:

R3-ATTACKER(config-if)#ipv6 nd dad attempts 0  ! DAD would prevent the spoof
R3-ATTACKER(config-if)#ipv6 address FE80::FA66:F2FF:FEDE:FF1 link-local

*Jul  4 14:45:01.109: %SISF-4-PAK_DROP: Message dropped A=FE80::FA66:F2FF:FEDE:FF1 G=- V=123 I=Gi1/3 P=NDP::RA Reason=More trusted entry exists

More trusted entry exists.  As you'll see above, there is a "Preflevel" numbering system, which I think of as administrative distance for neighbor bindings. The higher the number, the more trusted the entry is. Although I do find the message odd when you try to spoof -- what it really should say is "I already learned this entry from someone else, first come first serve!"

The most trusted method is a static entry, which looks like:

ipv6 neighbor binding vlan 123 FE80::FA66:F2FF:FEDE:FF1 interface gig1/3 c47d.4ff9.5340

You can shorten this up a whole bunch, by omitting the optional VLAN and MAC address, but in my case, I'm trying to override the legitimate link-local of R2 to allow R3 to ping from its link local. Without "fully populating" the table, it still prefs the dynamically learned entry over the static one.

We see the static entry with the priority of 100 - let's see if we can ping from R3 now, with our spoofed link-local:

R3-ATTACKER#ping FE80::F2F7:55FF:FE8D:96A2 source FE80::FA66:F2FF:FEDE:FF1
Output Interface: GigabitEthernet0/0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to FE80::F2F7:55FF:FE8D:96A2, timeout is 2 seconds:
Packet sent with a source address of FE80::FA66:F2FF:FEDE:FF1%GigabitEthernet0/0
Success rate is 100 percent (5/5), round-trip min/avg/max = 0/0/0 ms

Let's look at the rest of the features you can optionally put in a policy.

ipv6 nd inspection policy TEST-POLICY
 limit address-count X
This is relatively obvious, it limits how many addresses are permitted to participate in the ND process on a port. Keep in mind you always need a minimum of 2 if you have a global unicast: you need the link-local as well.
 tracking enable
This is liveliness tracking, and is pretty cool. We'll give this it's own paragraph below.
There's a cryptographic version of neighbor discovery called SeND. It requires a PKI infrastructure and certificates all the way down to the client. I'm going to call SeND "out of scope" for the CCIE v5 based on complexity. drop-unsecure is regarding SeND, so I'm skipping it.
 sec-level minimum 
Also used for SeND; out of scope.
 device-role {host | monitor | router} 
I'm not really sure what the point is here - I don't know how host or router would differ, and Cisco hasn't documented it.
 validate source-mac
I imagine this validates that the source MAC is appropriate for future ND communication, but it's not documented and I can't lab it without changing wires (my lab is remote, as mentioned above), so unfortunately I haven't got a good explanation on this option.

If you enable tracking, as shown above, hosts get probed with an "are you still there?" if no ND packets are heard periodically.

WS-C4948E(config)#ipv6 nd inspection policy ND-TEST
WS-C4948E(config-nd-inspection)#tracking enable
WS-C4948E(config)#vlan configuration 123
WS-C4948E(config-vlan-config)#ipv6 nd inspection attach-policy ND-TEST

Note the "time left" field on the far right. This is how long until a NS is sent out to the neighbor asking if it's still there. This is useful for two reasons:
- Since the table is first-come first-serve, this frees up address space from being held indefinitely if it's actually not in use.
- It allows for a host to move - imagine unplugging from one switchport and moving to another on the same switch - we need a way to age out the information reasonably quickly.

We see we have 220 and 228 seconds on the two hosts presently in the table until they're probed.

If we have an IPv6 address on the appropriate VLAN, we'll send a NS sourced from our IPv6 address.
It sure confused me - if we have a pure layer 2 device - how will it send an NS?

If we have a link local on the network, we'll send a NS from our IPv6 address. If we don't have an IPv6 address on the VLAN (we're just switching L2 only), we'll send an NS from the IPv6 unspecified address: that's right, the switch will send NSes even if IPv6 routing isn't enabled.

If you want probes to go out more frequently than every 300 seconds, you can set it like so:

ipv6 nd inspection policy TEST-POLICY
 tracking enable reachable-lifetime 15 ! this will set it for 15 seconds.

The most confusing feature for me was the "ipv6 snooping" syntax, which accomplishes basically everything we saw above, plus has the option (via source guard and destination guard) to filter data plane traffic as well. The confusing part about it is simple: it has lots of cross-over with all the previous functions, yet it uses a different syntax.

The basic concept of ipv6 snooping is to build the neighbor database, similar to what IPv6 ND inspection did, except it uses and enforces more methods all at once.

It can use:
- Information from DHCP (Default)
- Information from ND (Default)
- Static bindings

I'm wiping out all prior FH Security functions implemented above, we're starting from scratch with basic addressing. Since this is a large topic, I'm going to give increasingly more complicated examples and explain them one at a time.

At it's basics, enabling it is very simple:

WS-C4948E(config)#vlan configuration 123
WS-C4948E(config-vlan-config)#ipv6 snooping

Unfortunately this totally breaks the network.

By default, ipv6 snooping enables its version of RA Guard and ND Inspection, so now RAs won't work any longer.

*Jul  5 11:41:42.007: %SISF-4-PAK_DROP: Message dropped A=FE80::F2F7:55FF:FE8D:96A2 G=- V=123 I=Gi1/1 P=NDP::RA Reason=Packet not authorized on port

So let's fix that.

WS-C4948E(config)#ipv6 snooping policy TRUST_ROUTER
WS-C4948E(config-ipv6-snooping)# security-level glean
WS-C4948E(config-ipv6-snooping)#int gig1/1
WS-C4948E(config-if)#ipv6 snooping attach-policy TRUST_ROUTER

R2-HOST(config-if)#ipv6 address autoconfig default
R2-HOST(config-if)#do sh ipv6 int br | s GigabitEthernet0/0
GigabitEthernet0/0         [up/up]

Ok great! Now what the heck does "security-level glean" mean?  Let me tell you, it was a lot of 'fun' to figure out from the near zero explanation the docs give. glean learns from DHCP and ND, but doesn't enforce anything. So "glean" basically means "trust this port". There is also a trusted-port option on the policy, and on my IOS, it appears to do absolutely nothing (how handy).

For purposes of keeping this article a reasonable size, I'm going to go ahead and point out the 1:1 features in common with the a-la-carte ND inspection. By default, when you enable snooping, you're getting integrated ND Inspection. If you don't want it, you would:

WS-C4948E(config)#ipv6 snooping policy MY_POLICY
WS-C4948E(config-ipv6-snooping)#no protocol ndp

In this case, it would only learn entries from DHCPv6 "Guard" and static entries. Of note, you can do the same thing to disable dhcp inspection: no protocol dhcp

Assuming you left protocol ndp on, these features work the same way I explained them above:

limit address-count
  Make sure you allow at least 2 if you're using global unicast.
  Same as before, send NSes to keep the table up to date.

So let's look at the default integrated DHCP Guard inspection yet. As mentioned above, this is on by default when using IPv6 Snooping.

R1-ROUTER(config)#ipv6 dhcp pool DHCP-POOL
R1-ROUTER(config-dhcpv6)#address prefix 2001:100::/64
R1-ROUTER(config-dhcpv6)#dns-server 4::4
R1-ROUTER(config-dhcpv6)#dns-server 8::8
R1-ROUTER(config-dhcpv6)#int gig0/0
R1-ROUTER(config-if)#ipv6 dhcp server DHCP-POOL

Now I've already pre-trusted this port (with the glean security level mentioned above), so it's also OK to be a DHCP server.

R2-HOST(config)#int gig0/0
R2-HOST(config-if)#ipv6 enable
R2-HOST(config-if)#no ipv6 address autoconfig default
R2-HOST(config-if)#ipv6 address dhcp
R2-HOST(config-if)#do sh ipv6 int br | s GigabitEthernet0/0
GigabitEthernet0/0         [up/up]

We got our DHCP address.

And we now learned a binding via "DH" - DHCP.

There are three security models that can be applied to entire VLANs or per-port/per-vlan:

WS-C4948E(config-ipv6-snooping)#security-level ?
  glean    Glean addresses
  guard    inspect and drop un-authorized messages (default)
  inspect  glean and Validate message

We've discussed glean - it basically trusts the port but still keeps track of the bindings. Guard, as indicated above, is the default, and it's what we're getting when nothing is specified. Guard is, in effect, the same as enabling the a-la-cart DHCP Guard, RA Guard, and ND Inspection: It learns bindings and denies untrusted (non-glean) ports from sending RAs, DHCP offers, invalid DAD, invalid NAs, etc. It's an all-in-one control-plane security enforcer! 

Best I can tell, "inspect" enforces ND only (similar to the a-la-carte ipv6 nd inspection feature) but doesn't protect against malicious RAs or DHCP servers. I validated this by enabling it on all interfaces, sending RAs and DHCP off R1, and then attempting to spoof R2's address on R3's interface. Everything was permitted except the R3 spoof of R2, which produced:

*Jul  5 13:38:48.559: %SISF-4-PAK_DROP: Message dropped A=2001:100::9193:876E:7E83:F33C G=- V=123 I=Gi1/3 P=NDP::NA Reason=More trusted entry exists

on the 4948.

That wraps up the control-plane filters, data-plane filters are next, including Source Guard, Destination Guard, and Prefix Guard. IPv6 snooping builds the database that these features use, so it is a prerequisite for everything we'll see from here on.

Source guard is very simple. If the data-plane traffic - any traffic other than IPv6 ND/RA and DHCP - doesn't match the source address present in the prebuilt binding table, drop the traffic.

WS-C4948E(config)#vlan configuration 123
WS-C4948E(config-vlan-config)#ipv6 source-guard

The main additional configuration for this is to validate prefixes, which is what's known as Prefix Guard.

Far earlier in the article we discussed prefix delegations via DHCP. This is the technology that allows you to sub-lease a prefix from one DHCP server to a downstream DHCP server. I couldn't get this feature to work with my lab, and I believe it's a platform limitation, but unfortunately when you're borrowing high-end switches to lab bleeding-edge functions, beggars can't be choosers.

Here is how I think it's supposed to work:

ipv6 local pool dhcpv6-pool1 2001:100:123::/40 48

ipv6 dhcp pool test
 prefix-delegation pool dhcpv6-pool1 lifetime 1800 600
 dns-server 4::4
 dns-server 8::8 
 domain-name servers.net

interface GigabitEthernet0/0
 ipv6 address 2001:100:123::1/48
 ipv6 nd other-config-flag
 ipv6 dhcp server test 

interface GigabitEthernet0/0
 ipv6 address autoconfig default
 ipv6 nd ra suppress all
 ipv6 dhcp client pd PREFIX-DELEGATION

interface Loopback0  ! I needed something to pretend to be a downstream client
 ipv6 address PREFIX-DELEGATION ::/64 eui-64  
 ipv6 enable

ipv6 snooping policy SNOOPING-POLICY
 security-level glean
 prefix-glean  ! this learns prefix delegations - this did work for me, it was the filtering that didn't work.

ipv6 source-guard policy PREFIX-GUARD
 no validate address
 validate prefix

vlan configuration 123
 ipv6 snooping attach-policy SNOOPING-POLICY
 ipv6 source-guard attach-policy PREFIX-GUARD

And after applying all this, my switch shot back at me:

WS-C4948E(config)#%warning% This filter is not supported. Vlan - 123,  mac - any, prefix_length - 64%warning% This filter is not supported. Vlan - 123,  mac - any, prefix_length - 64%warning% This filter is not supported. Vlan - 123,  mac - any, prefix_length - 128  

I tinkered with it for a while, changing the address to another one on Lo0 that I shouldn't be able to use, to no avail. Traffic kept passing.  So I'm assuming this just can't be accomplished on this hardware or I'm hitting a bug.

I did have one other curiosity with source guard (just vanilla source guard, not prefix guard); while it did filter traffic appropriately, sometimes when I disabled the feature, traffic still wouldn't forward. Clearing the database solved the problem: clear ipv6 neigh bind

Our last major topic is Destination Guard, which is pretty darn cool. The recommended prefix size for a LAN segment in IPv6 is a /64, which is 18 quintillion addresses. To put that into perspective, that's 18,446,744,073,709,551,616 addresses in one segment.

What would happen if you tried to "arp" (IPv6 NS) for 18,446,744,073,709,551,616 addresses back-to-back? 

Your router would melt. You'd run out of RAM and CPU very quickly. At a best case this would result in a simple DOS where legitimate NSes couldn't get through for a while, at a worst case the OS might crash. Most of the attacks we've looked at so far require the attacker to have access to the "inside" of your network, what's worse about this attack is that it can be accomplished from the outside of your network - potentially from the Internet.

Destination Guard addresses this. Destination Guard is a "last hop" security feature: the attack can be launched from anywhere on a routed network, and the last hop router is the only one that is heavily impacted, because interim routers don't have to NS for the final destination, they just CEF-switch the packet.

We're also assuming, for our purposes here, that the last-hop router is a layer 3 switch supporting destination guard.

WS-C4948E(config)#vlan configuration 123
WS-C4948E(config-vlan-config)#ipv6 destination-guard

There's only one very simple setting you can add if you use a policy:

WS-C4948E(config)#ipv6 destination-guard policy FOO
WS-C4948E(config-destguard)#enforcement ?
  always    Enforced under all conditions (default)
  stressed  Enforced when system is under stress

stressed isn't defined anywhere, but I'm assuming it means only kick this feature in during high CPU load, or perhaps under a great deal of NS.

I'll be changing our lab up a bit in order to test this, R1 will be out of the picture, R2 will have a static assignment and will be our "trusted" host, and R3 will be our attacker on the Internet.

R2-HOST(config-if)#int gig0/0
R2-HOST(config-if)#ipv6 address 2001:600D::2/64 ! my best representation of "GOOD" in hex :)
R2-HOST(config)#ipv6 route ::/0 2001:600D::1  ! the switch's address

R3-ATTACKER(config-if)#int gig0/0
R3-ATTACKER(config-if)#ipv6 address 2001:BAD::2/64
R3-ATTACKER(config)#ipv6 route ::/0 2001:BAD::1

WS-C4948E(config)#ipv6 unicast-routing
WS-C4948E(config)#vlan 200
WS-C4948E(config)#vlan 300
WS-C4948E(config)#int vlan200
WS-C4948E(config-if)#ipv6 address 2001:600D::1/64
WS-C4948E(config-if)#no shut
WS-C4948E(config-if)#int vlan 300
WS-C4948E(config-if)#ipv6 address 2001:BAD::1/64
WS-C4948E(config-if)#no shut
WS-C4948E(config)#int gig1/2
WS-C4948E(config-if)#switchport access vlan 200
WS-C4948E(config)#int gig1/3
WS-C4948E(config-if)#switchport access vlan 300

WS-C4948E(config-if)#vlan configuration 200,300
WS-C4948E(config-vlan-config)# ipv6 snooping
WS-C4948E(config-vlan-config)# ipv6 destination-guard

Now let's say the "BAD" neighbor, a host on the Internet, tries to hammer away at tens of thousands of addresses on the "GOOD" (600D) network in order to make the router (our 4948) collapse under the NS load.

In order to see this security feature take effect, we need to enable one more logging feature:

WS-C4948E(config)#ipv6 snooping logging resolution-veto

What the heck is resolution veto? If the switch decides it's getting asked to resolve for bogus addresses, it will "veto" the neighbor solicitation.

Before I try the attack, I went ahead and verified connectivity from R2 to R3. 

R2-HOST(config)#do ping 2001:BAD::2

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2001:BAD::2, timeout is 2 seconds:

That should've worked. What did the 4948 have to say?

*Jul  5 16:13:12.774: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:BAD::2 on I=Vl300 reason=Destination not active on link

This is an important point on this feature: it doesn't know what hosts are valid until it hears from them. So due to my order-of-operations in this scenario, the switch hadn't actually heard from R3 at all yet, and the NS is denied.  Making R3 speak solves the problem, which would've worked itself out eventually anyway:

R3-ATTACKER(config)#do ping 2001:600D::2
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2001:600D::2, timeout is 2 seconds:
Success rate is 100 percent (5/5), round-trip min/avg/max = 0/1/8 ms

Now let's try our theoretical attack.

R3-ATTACKER#ping 2001:600D::100 repeat 1 timeout 0
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 2001:600D::100, timeout is 0 seconds:
Success rate is 0 percent (0/1)
R3-ATTACKER#ping 2001:600D::101 repeat 1 timeout 0
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 2001:600D::101, timeout is 0 seconds:
Success rate is 0 percent (0/1)
R3-ATTACKER#ping 2001:600D::102 repeat 1 timeout 0
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 2001:600D::102, timeout is 0 seconds:
Success rate is 0 percent (0/1)
R3-ATTACKER#ping 2001:600D::103 repeat 1 timeout 0
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 2001:600D::103, timeout is 0 seconds:
Success rate is 0 percent (0/1)

(pretend there are 65,431 hypothetical pings here)

R3-ATTACKER#ping 2001:600D::FFFF repeat 1 timeout 0
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 2001:600D::FFFF, timeout is 0 seconds:
Success rate is 0 percent (0/1)

We expected that outcome as these aren't valid hosts, but what happened on the switch?

*Jul  5 16:18:26.226: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:600D::100 on I=Vl200 reason=Destination not active on link
*Jul  5 16:18:34.146: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:600D::101 on I=Vl200 reason=Destination not active on link
*Jul  5 16:18:43.338: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:600D::102 on I=Vl200 reason=Destination not active on link
*Jul  5 16:18:50.418: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:600D::103 on I=Vl200 reason=Destination not active on link
*Jul  5 16:18:58.958: %SISF-4-RESOLUTION_VETO: Resolution vetoed NS for D=2001:600D::FFFF on I=Vl200 reason=Destination not active on link

And that about sums up destination guard.

A few random notes for the wrap-up.

Some useful show commands:

WS-C4948E#sh ipv6 snooping policies
Target               Type  Policy               Feature        Target range
Gi1/2                PORT  policy1              NDP inspection vlan all

This will show you what policies are applied where.

WS-C4948E#sh ipv6 snooping features
Feature name   priority state
NDP inspection    160   READY
Snooping          128   READY
This shows which features are enabled.

And where is all this in the documentation?

Well, at the time of this writing, there's a link to all this directly under the IOS 15.2E, which is what I labbed on! and ... it's a broken link! Uuuuurgh!

Best I could come up with that I could drill-down to:

Switches -> 
3850 -> 
Catalyst 3850-12S-E Switch -> 
Configuration Guides -> 
IPv6 Configuration Library, Cisco IOS XE Release 3SE (Catalyst 3850 Switches) -> 
IPv6 First-Hop Security Configuration Guide, Cisco IOS XE Release 3SE (Catalyst 3850 Series)

That covers just about everything except Source Guard and Prefix Guard, but let's face it, those two features are pretty easy to understand if you have the rest of it down.

Best of luck!