Showing posts with label filtering. Show all posts
Showing posts with label filtering. Show all posts

Tuesday, October 4, 2016

Cisco Routing Oddities -- Interaction Between EIGRP, Distribute-List, and Static Routes

I ran into something at work today that I didn't quite understand, and which I finally had to mock up in GNS-3 to figure out. While trying to track down a routing problem, I found a distribute-list statement on an EIGRP configuration that referenced an access list which denied anything from 10.0.0.0/8. That's not terribly unusual, except that it looked like there were still a lot of 10-dot subnets being propagated by EIGRP on this router. Digging into the configuration a bit more, I saw that there was a "redistribute static" command inside the EIGRP configuration, and I found several static routes to 10-dot subnets. To understand the interaction between EIGRP, static routes, and the "distribute-list" statement, I created the following, simple network in GNS-3:



In this network, R1, R3, R4 and R5 will all use EIGRP to share routes with each other. R2 will have a default route pointing to R1, and R1 will have a single static route to R2's loopback address:
R1(config)#ip route 10.254.254.0 255.255.255.0 10.0.0.2
R1(config)#router eigrp 10
R1(config-router)#redistribute static metric 1000000 1 255 1 1500

After turning up EIGRP, R1 shared all of its known routes with R3, R4 and R5, exactly as you would expect:
R2#ping 100.64.254.1
Sending 5, 100-byte ICMP Echos to 100.64.254.1, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 4/6/8 ms
R2#

-----------------------------------------------------------------------

R3#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 8/17/44 ms
R3#

-----------------------------------------------------------------------

R4#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 12/29/72 ms
R4#

-----------------------------------------------------------------------

R5#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 12/27/44 ms
R5#

Then, I added the following configuration statements to R1:
R1(config)#ip access-list standard FILTER-NULL-ROUTES
R1(config-std-nacl)#deny 10.0.0.0
R1(config-std-nacl)#permit any
R1(config-std-nacl)#!
R1(config-std-nacl)#router eigrp 10
R1(config-router)#distribute-list FILTER-NULL-ROUTES out
R1(config-router)#!

Running the exact same ping tests...:
R2#ping 100.64.254.1
Sending 5, 100-byte ICMP Echos to 100.64.254.1, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 4/12/40 ms
R2#

-----------------------------------------------------------------------

R3#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R3#

-----------------------------------------------------------------------

R4#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R4#

-----------------------------------------------------------------------

R5#ping 10.0.0.2
Sending 5, 100-byte ICMP Echos to 10.0.0.2, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R5#

This is to be expected -- 10.0.0.0/30 is a control to verify that the distribute-list is working as expected (that is, that it is filtering 10.0.0.0/8 from the EIGRP updates). Let's see if the distribute-list overrides the static route, or if the static route overrides the distribute-list:
R3#ping 10.254.254.9
Sending 5, 100-byte ICMP Echos to 10.254.254.9, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 12/23/40 ms
R3#

-----------------------------------------------------------------------

R4#ping 10.254.254.9
Sending 5, 100-byte ICMP Echos to 10.254.254.9, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 8/35/72 ms
R4#

-----------------------------------------------------------------------

R5#ping 10.254.254.9
Sending 5, 100-byte ICMP Echos to 10.254.254.9, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 16/28/48 ms
R5#

Notice how we can still reach 10.254.254.9, even though we can no longer reach 10.0.0.2? The distribute-list is filtering 10.0.0.0/8 addresses from the EIGRP routing updates sent by R1. However, we have explicitly stated that we want static routes to be redistributed by EIGRP, and we have a static route for 10.254.254.0/24 via 10.0.0.2. Consequently, R1 is still advertising a route for 10.254.254.9, despite the distribute-list filter. This explains how my router at work was still sharing 10-dot routes with other routers in its EIGRP area, even though there was a distribute-list statement filtering 10-dot subnets.

Tuesday, June 7, 2016

BGP Route Filtering

Consider the network in the following diagram...:

If you apply a simple BGP configuration to each router in this network, something along the lines of...:
#R5 BGP Configuration:
router bgp 500
no synchronization
bgp router-id 10.254.254.5
bgp log-neighbor-changes
network 10.254.254.5 mask 255.255.255.255
network 172.16.0.0 mask 255.255.255.252
network 172.16.0.16 mask 255.255.255.252
neighbor 172.16.0.2 remote-as 100
neighbor 172.16.0.17 remote-as 400
no auto-summary

...BGP will find the "best" route around the network. For example, for R1 to ping R4's loopback address, it would take the route R1, R5, R4, and to ping R3's loopback address, R1 would take the route R1, R2, R3:
R1#sho ip route
<---snip--->
10.0.0.0/32 is subnetted, 5 subnets
B 10.254.254.2 [20/0] via 172.16.0.6, 00:00:10
B 10.254.254.3 [20/0] via 172.16.0.6, 00:00:10
C 10.254.254.1 is directly connected, Loopback0
B 10.254.254.4 [20/0] via 172.16.0.1, 00:00:09
B 10.254.254.5 [20/0] via 172.16.0.1, 00:00:15
R1#

Exactly as you would expect, right?

It could happen, however, that there is some factor of which BGP is unaware that makes this routing less than optimal. Suppose, for example, that you are the administrator of router R5, and while you would like to multi-home your router, you don't want to allow other networks to transit your router. To prevent BGP from advertising routes to other networks, it is possible to filter outbound routes from your router.

Step 1: Create an access list to define the networks you want to allow outbound.
R5(config)#access-list 1 permit 10.254.254.5 0.0.0.0
R5(config)#access-list 1 permit 172.16.0.0 0.0.0.3
R5(config)#access-list 1 permit 172.16.0.16 0.0.0.3

Step 2: Edit the BGP config to filter the routes as defined in your access list.
R5(config)#router bgp 500
R5(config-router)# neighbor 172.16.0.2 distribute-list 1 out
R5(config-router)# neighbor 172.16.0.17 distribute-list 1 out

Step 3: Clear the BGP sessions to reset the routes.
R5#clear ip bgp 100
R5#clear ip bgp 400
R5#
00:49:41: %BGP-5-ADJCHANGE: neighbor 172.16.0.2 Down User reset
R5#
00:49:43: %BGP-5-ADJCHANGE: neighbor 172.16.0.17 Down User reset
R5#

Now, if you look at the routes on R1, it will only transit R1 to reach networks that are directly connected to R1:
R1#sho ip route
<---snip--->
172.16.0.0/30 is subnetted, 5 subnets
B 172.16.0.16 [20/0] via 172.16.0.1, 00:01:28
B 172.16.0.12 [20/0] via 172.16.0.6, 00:09:24
B 172.16.0.8 [20/0] via 172.16.0.6, 00:09:24
C 172.16.0.4 is directly connected, FastEthernet0/0
C 172.16.0.0 is directly connected, FastEthernet3/0
10.0.0.0/32 is subnetted, 5 subnets
B 10.254.254.2 [20/0] via 172.16.0.6, 00:09:24
B 10.254.254.3 [20/0] via 172.16.0.6, 00:09:24
C 10.254.254.1 is directly connected, Loopback0
B 10.254.254.4 [20/0] via 172.16.0.6, 00:02:21
B 10.254.254.5 [20/0] via 172.16.0.1, 00:01:28
R1#

As you can see, to reach R4's loopback address now, R1 now will route traffic through R2 rather than through R5.

Now, what would happen if there was a break somewhere in between R2 and R4?
R2#conf t
R2(config)#int fa0/0
R2(config-if)#shut

R1#ping 10.254.254.4

Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.254.254.4, timeout is 2 seconds:
.....
Success rate is 0 percent (0/5)
R1#

R5 will not propagate any routes except those that you have explicitly allowed, and as a result, R1 no longer has a route to R4.

Cisco Intro to QoS and CoS, Part 1 -- Identifying, Classifying and Marking Traffic

One problem found in any network of reasonable size is how to handle congestion. When there is more bandwidth available than clients require, there is (of course!) no problem. However, some types of network traffic -- such as streaming media -- are particularly bandwidth intensive, and often, very sensitive to variations in delay time. Consider, for example, HTTP traffic: an end user will most likely not notice the variation in transit times ("jitter") of the various packets that make up a requested download. On the other hand, a Voice over IP call is very sensitive to jitter. If the packets making up a Voice over IP call or videoconference don't arrive within a reasonably consistent timeframe, the call quality will suffer, and your end users will not be happy. Even worse, what would happen if your dynamic routing protocols cannot send updates and keepalive traffic in a timely fashion? Clearly, there needs to be a way to prioritize certain types of traffic to ensure that your clients receive sufficient bandwidth for their needs.

Consider the following network:


On the left-hand side of the network drawing, we have three Linux hosts, clients of the server on the right-hand side of the drawing. These three hosts will be in competition with each other for network resources, and because we are running OSPF (or routing protocol of your choice -- and yes, a dynamic routing protocol is overkill on a network this simple, but stay with me...) on R1 and R2, they will be competing with the network clients for bandwidth as well. Making this problem even more apparent, I have connected all of the PC's to the network via FastEthernet interfaces, but only connected R1 and R2 with a single T1 line. Due to the extreme mismatch between LAN and WAN capabilities, any one of the network clients can easily saturate the "WAN" link between R1 and R2. Don't believe me? Let's do some tests...

On the host "CentOS6," I have configured the "echo" service, and on the host "CentOS6_Clone," I have installed a network diagnostic tool called "tcpspray." With no other traffic on the network, let's use tcpspray to see how long it takes to send a flurry of data to the "CentOS6" server:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 25.670507 seconds (30.955 kbytes/s)
Transmitted 1024000 bytes in 25.146152 seconds (39.768 kbytes/s)
[root@centos6_clone ~]#

Also on the host "CentOS6," I am running a web server, and one of the web pages hosted on the server is an image gallery that loads a 5x5 array of JPEG images (get your mind out of the gutter! They're pictures of motorcycles that inspired me while I was building my Cafe Racer project!). How would the tcpspray bandwidth be affected if one of the Knoppix clients was downloading the images to populate the web page at the same time? Let's find out...:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 118.753065 seconds (8.421 kbytes/s)
Transmitted 1024000 bytes in 104.348504 seconds (9.583 kbytes/s)
[root@centos6_clone ~]#

Wow...it took almost five times as long to receive the replies, due to the congestion on the network! Fortunately, quality of service and/or class of service policies can allow us to prioritize certain kinds of traffic, as our network needs dictate.

Keep in mind that nothing is free, however. If we prioritize some kinds of traffic, that necessarily comes at the expense of other traffic. Therefore, you have to have a good idea what kinds of traffic cross your network, and how they should be balanced against each other...which is a good place for us to begin with our policy. Let's define the traffic that we want prioritized.

As I mentioned earlier, the parameters I used for tcpspray specified that we would use the "echo" service for our traffic. Let's include SSH as priority traffic so we can manage our routers (you are using SSH for network management, right?). As I also mentioned earlier, our network control traffic is also high priority. Too keep the discussion simple for now, we'll assume everything else is "best effort."

Now that we have a good idea how we want to prioritize our traffic, we need a way to have the router filter our priority traffic. If you think that sounds like a job for an Access Control List, you're exactly right:

ip access-list extended CORE_GOLD
 permit tcp any eq echo any
 permit tcp any any eq echo
!
ip access-list extended CUST_EF
 permit tcp any eq 22 any
 permit tcp any any eq 22
!

After identifying the priority traffic with an ACL, we then create "class maps" to map that traffic into different classes (kind of sounds like a tautology, sorry!):

class-map match-any CORE_CONTROL
 description Network control OSPF/BGP/etc
 match ip precedence 6
 match ip precedence 7
class-map match-any CUST-EF
 match access-group name CUST_EF
class-map match-any CORE_GOLD
 description Low-latency/priority queue
 match access-group name CORE_GOLD
!

We have now created three classes of priority traffic:
  1. CORE_CONTROL, for our network control traffic,
  2. CUST-EF, for high priority traffic ("expedited forwarding"), and
  3. CORE_GOLD, for low-latency traffic.

Our next step is to use these maps to define how the different classes of traffic will be handled:

policy-map CUST-IN
 class CUST-EF
  set ip dscp ef
 class class-default
  set dscp cs3
policy-map CUST-Generic
 class CORE_GOLD
  priority percent 50
 class CORE_CONTROL
  bandwidth percent 10
 class class-default
  bandwidth percent 1
!

For our "CUST-EF" traffic, we have said that we want to place a special marking, "dscp ef," on the packets that match this traffic. This gives our "CUST-EF" traffic high priority on the network. For "CORE_GOLD" traffic, we have said that we want to give that traffic 50 per cent of the available bandwidth. For "CORE_CONTROL" traffic, we have said that we want to give such traffic 10 per cent of the available bandwidth (network control traffic is typically small-volume, bursty traffic, so it is not necessary to reserve a large amount of bandwidth for this traffic -- we just need to ensure that the traffic isn't starved for bandwidth during times of congestion). Notice that we also have another de facto class of traffic in the policy maps: class-default. This is for traffic that didn't match any of our access control lists. In the "CUST-IN" policy map, we mark it with "dscp cs3" -- a low priority traffic queue, and in the "CUST-Generic" policy map, we don't reserve any bandwidth for this class.

At this point, we have established the framework for our QoS/CoS configuration, but we haven't actually implemented it yet. Just as an access-control list used to filter certain kinds of traffic has to be applied to an interface before it is useful, so does a policy map:

interface Serial1/0
service-policy output CUST-Generic
!

We also will apply the "CUST-IN" policy map to the ingress interface, VLAN 10 on R1 and Fa0/0 on R2:

R1(config)#int vlan 10
R1(config-if)#service-policy input CUST-IN
R1(config-if)#

R2(config)#int fa0/0
R2(config-if)#service-policy input CUST-IN
R2(config-if)#

Now, let's run our tcpspray test again, while the Knoppix client reloads the web page with the 25 JPEG images:

[root@centos6_clone ~]# tcpspray -e -n 1000 192.168.2.2
Received 1024000 bytes in 78.483684 seconds (12.742 kbytes/s)
Transmitted 1024000 bytes in 69.900585 seconds (14.306 kbytes/s)
[root@centos6_clone ~]#

These results are still slower than our initial baseline test, with no congestion on the network. However, it is significantly better than the test with no QoS/CoS and a congested network.

Monday, July 14, 2014

Advanced Cisco Routing -- Route Maps

So far, all of our routing examples have been pretty straight-forward: to reach network "A" you take route "A," and to reach network "B" you take route "B," etc. Suppose, however, that you need to filter routes between different WAN sites? Specifically, consider the following list of requirements:

  1. Multiple WAN sites with RFC-1918 IP space on the inside of the networks;
  2. Direct Nat'ed Internet links at each WAN site;
  3. Point-to-Point links (T1's, VPN tunnels, etc.) between WAN sites to route the internal LANs together;
  4. OSPF routing between internal LAN subnets.


In this case, you can use a route map to filter what networks OSPF is advertising. Here's how you do it:

First, here is the network topology for our example:
In this drawing, we'll use router R1 as our Anchorage router, R2 as our Fairbanks router, R4 and R5 will be random hosts on the internal LANs of R1 and R2 (respectively), and R3 will represent the Internet network between R1 and R2. Here is the basic configuration for R1 (the Anchorage router):

interface FastEthernet0/0
description LAN
ip address 192.168.1.1 255.255.255.0
ip nat inside
duplex auto
speed auto
!
interface FastEthernet1/0
description Inet
ip address 169.254.1.10 255.255.255.192
ip nat outside
duplex auto
speed auto
!
interface FastEthernet2/0
description OVPN to Fairbanks
ip address 172.16.1.1 255.255.255.252
ip nat inside
duplex auto
speed auto
!
ip nat inside source list 1 interface FastEthernet1/0 overload
no ip http server
no ip http secure-server
ip classless
ip route 0.0.0.0 0.0.0.0 169.254.1.1
!
access-list 1 permit 192.168.1.0 0.0.0.255
access-list 1 permit 172.16.1.0 0.0.0.3
!

As you can see, we have set up NAT to map addresses on the two internal interfaces to the external IP address of 169.254.1.10, and we have created a default route to the Internet router at 169.254.1.1. At this point, any hosts on the internal network should be able to reach any publicly-accessible IP address.

Similarly, here is the basic configuration for the Fairbanks router:

interface FastEthernet0/0
description LAN
ip address 192.168.3.1 255.255.255.0
ip nat inside
duplex auto
speed auto
!
interface FastEthernet1/0
description Inet
ip address 169.254.2.10 255.255.255.192
ip nat outside
duplex auto
speed auto
!
interface FastEthernet2/0
description OVPN to Anch
ip address 172.16.1.2 255.255.255.252
ip nat inside
duplex auto
speed auto
!
ip nat inside source list 1 interface FastEthernet1/0 overload
no ip http server
no ip http secure-server
ip classless
ip route 0.0.0.0 0.0.0.0 169.254.2.1
!
access-list 1 permit 192.168.3.0 0.0.0.255
access-list 1 permit 172.16.1.0 0.0.0.3
!

With this configuration, R4 should be able to ping the outside interface on R2, and R5 should be able to ping the outside interface on R1 (assuming that R3, R4 and R5 have been configured with the appropriate IP addresses on their respective interfaces, and that R4 and R5 have default routes through R1 and R2, respectively). However, if you try to ping R5 from R4 or vice versa, you will find that the pings fail, because R1 and R2 are not yet advertising routes to their internal networks. You could create static routes on these routers to solve this problem, but that's why we have dynamic routing protocols -- to reduce the network admin's workload.

However, you don't want to send routes to public IP addresses over your internal-only links, and even more importantly, you don't want to advertise your RFC-1918 IP addresses on the public Internet (your service provider should already be filtering these, but...). Therefore, we want to make sure OSPF is only advertising our private LAN addresses over the point-to-point link, so we'll create a route map on R1 and R2 to filter what routes OSPF advertises.

The first step in creating a route map is creating the access control list (ACL) to identify the traffic we want OSPF to allow. On R1...:

access-list 10 permit 192.168.3.0 0.0.0.255
access-list 10 permit 192.168.1.0 0.0.0.255
access-list 10 permit 172.16.1.0 0.0.0.3
!

...and on R2...:
access-list 10 permit 192.168.3.0 0.0.0.255
access-list 10 permit 192.168.1.0 0.0.0.255
access-list 10 permit 172.16.1.0 0.0.0.3
!

Note: If you don't fully understand the ACL's we have created here, check out CCNA Lesson 12 for a more in-depth explanation of ACL's on a Cisco router.

Next, we create the route-map to allow the networks filtered by these access lists (configuration is the same on both R1 and R2):
route-map inside-ospf permit 10
match ip address 10
!

Finally, we create our OSPF configuration, referencing the route maps. On R1:

router ospf 42
router-id 192.168.1.1
log-adjacency-changes
redistribute connected subnets route-map inside-ospf
network 172.16.1.0 0.0.0.3 area 0.0.0.0
!

...and on R2:

router ospf 42
router-id 192.168.3.1
log-adjacency-changes
redistribute connected subnets route-map inside-ospf
network 172.16.1.0 0.0.0.3 area 0.0.0.0
!


Here's what's happening in the router. We are creating the ACL to match our internal LAN traffic (192.168.1.0/24, 192.168.3.0/24, and 172.16.1.0/30). Then, we are creating a route map called "inside-ospf" to match the networks defined in ACL 10. Finally, OSPF is redistributing the networks referenced in the route-map "inside-ospf." If a network does not match the ACL, for example 169.254.1.0/26 (the network attached to fa1/0 on router R1), OSPF does not forward that route to other OSPF-enabled routers in that area. You can verify that this is working by tracing routes to various networks from R4 or R5. Here is an example of two traceroutes from R5:

Router#traceroute 192.168.1.2

Type escape sequence to abort.
Tracing the route to 192.168.1.2

1 192.168.3.1 8 msec 8 msec 8 msec
2 172.16.1.1 24 msec 8 msec 16 msec
3 192.168.1.2 28 msec 16 msec *
Router#traceroute 169.254.1.10

Type escape sequence to abort.
Tracing the route to 169.254.1.10

1 192.168.3.1 8 msec 4 msec 8 msec
2 169.254.2.1 16 msec 12 msec 8 msec
3 169.254.1.10 28 msec 25 msec *
Router#

You can see the traceroute follows the P-t-P link from R2 to R1 to reach 192.168.1.2, but follows the "Internet" link to reach the outside interface of R1, which is exactly what we wanted.

Reference:Cisco's "Route Maps for IP Routing Protocol Redistribution Configuration" web page.

Wednesday, December 11, 2013

Configuring Port Security on a Foundry/Brocade FastIron Switch

I recently spoke with a user at a remote site on my network who was running into trouble getting on-line. During the troubleshooting session, I asked him to retrieve his IP address, and he gave me an address that was on a different subnet than I expected for his location. Puzzled, I asked a few follow-up questions, and found that he had connected a wireless router to my LAN switch at the remote site, a violation of our corporate compliance policies. In general, I'm not a fan of "Big Brother" IT/MIS policies, but in this case, I got a little torqued. This user is not MIS, he is not the guy who will get called out on the carpet if our network is breached because he didn't properly secure his wireless access point, and therefore, he is not supposed to be connecting wireless devices without explicit approval and knowledge of either me or one of the other network admins.

Unfortunately, this is not an uncommon occurrence. Fortunately, the Foundry FES2402 switches that we are using at these remote sites gives the network admin(s) some tools to help prevent users from connecting unauthorized devices to our networks.

The first tool is a simple MAC filter. In this case, there is a fixed number of devices that are supposed to be connected to our network, the MAC addresses of these devices are known, and therefore, we can create a filter to allow only these MAC addresses on a given port(s). Alternatively, if there is a known MAC address (or multiple addresses) that we *don't* want connected to a given port, we can create a filter to disallow it (or them). Here's how you do it:

  conf t
  mac filter 1 deny 0015.c507.ae6b ffff.ffff.ffff any
  mac filter 128 permit any any
    interface ethernet 7
      mac filter-group 1 128
      mac filter-group log-enable

This filter denies access from MAC address 0015.c507.ae6b to eth7, but allows access from all other MAC addresses.

Keep in mind that there are a few tips and a couple of "gotchas" in the simple MAC filter. First, just as when creating an Access Control List (ACL) or firewall rule for an IP address, you can filter for a portion of the MAC address, if you like:

  conf t
    mac filter 1 deny 0015.c507.ae6b ffff.0000.0000 any
    mac filter 128 permit any any
    interface ethernet 7
      mac filter-group 1 128
      mac filter-group log-enable

This will only match the "0015" portion of the given MAC address.

For a "gotcha," while there are many interface parameters that you can set using a range of Ethernet ports on a Foundry/Brocade switch, this isn’t one of them. For example, this…:

  conf t
  int eth 1 to 24
    mac filter-group 1 128
    mac filter-group log-enable

…doesn’t work. You can only apply a filter to a single interface at a time.

Another "gotcha" is that if you create multiple filters, then when applying the filters to an interface, you MUST include all of the filter groups on one line. If you try to put the filter groups on multiple lines, you will only get the LAST filter:

  conf t
  mac filter 1 deny 0015.c507.ae6b ffff.ffff.ffff any
  mac filter 128 permit any any
  int eth 7
      mac filter-group 1
      mac filter-group 128
      mac filter-group log-enable

This will result in ONLY filter 128 ("permit any any") being applied to the interface -- NOT AT ALL what you (presumably) intended.

The second tool is a lock that allows the admin to limit the number of devices that can access a given port on the switch. Unfortunately, this only sends an SNMP trap for a violation -- it does not actually disable the port or drop traffic from an unauthorized MAC address. Here's how you do it:

  conf t
  lock ethernet 15 addr-count 1

This applies a "lock" to allow only a single MAC address to eth 15 on a FES2402 switch. However, this isn't a terribly useful tool, as it only notifies the admin when someone attempts to attach an unauthorized device.

However, there is a third tool that takes the "lock" concept and gives it some teeth, so to speak. This is the MAC Port Security feature, and it can be applied either globally or directly to an Ethernet interface. Here is how you apply the MAC Port Security feature to a specific interface:

  conf t
  int eth 0/1/15
    port security
      enable
      maximum 1
      age 5
      violation restrict

In this example, we applied Port Security to Ethernet 0/1/15, enabled port security, set a maximum of 1 authorized IP address to this port, set the aging timer to drop the authorized IP address after 5 minutes (that is, if five minutes elapse without receiving a frame from a device, it will clear the MAC address entries for the interface, allowing a new device to connect), and telling the port to drop frames from any unrecognized device. You can set up to a maximum of 64 known MAC addresses per port. The aging timer can be set from zero (never time out a recognized MAC address) to 1440 minutes. The admin can choose between "restrict" and "shutdown" when a violation occurs; restrict will simply drop frames from an unknown device, whereas shutdown will disable the Ethernet interface for a specified period. If you choose "shutdown", then the next parameter is a number between 0 and 1440, where zero means shut down the port permanently, and any other number is the time to shut down the port, in minutes.

In addtion, you can manually specify allowed MAC addresses on the port, or you can configure to automatically discover and save allowed MAC addresses to the startup configuration. To manually specify an allowed MAC address:

  conf t
  int eth 0/1/15
    port security
      enable
      secure 0123.4567.89ab

This configuration manually specifies that only the device with the MAC address "0123.4567.89ab" should be allowed to access the port.

Alternatively, to have the Foundry auto-discover an allowed device, then write the MAC address to the startup configuration:

  conf t
  int eth 0/1/15
    port security
      enable
      autosave 60

This will automatically detect the MAC address of the device connected to the port and write its MAC address to the startup configuration every 60 minutes. Only that device will be allowed to connect from then on.

Thursday, November 14, 2013

JNCIA Lesson 4 1/2 -- More on Routing Policies and Firewall Filtering

One of the concepts that I have struggled with the most while trying to learn JunOS is the filtering language used on Juniper devices. That's a pretty significant problem too, because the filtering language is basically a core feature of JunOS, cropping up in Class of Service/Quality of Service, routing policies, and, of course, basic firewall/security on JunOS devices. It's pretty safe to say that if you don't understand this core concept, you will not be proficient in JunOS, and therefore, I decided I needed to spend a little while playing with the filtering syntax so I can understand it better. In it's most basic form, the filtering rules follow the pattern, "match-action" -- that is, you define a pattern to match, then upon a match, you perform some action on incoming packets -- and then you apply the filter to an interface or VLAN. There are three important concepts to keep in mind when trying to compose a filter rule:
  1. Within a single term, match rules are logical-AND'ed together. That is, if you put a rule to match a source IP address inside a term, then put a second rule to match a destination port, the filter will only match packets from that particular source IP address AND to that particular destination port (i.e., from 192.168.1.17, to port 23). If you want to logically OR rules, you have to create separate terms in the firewall rule-set;
  2. Packets are compared to each term from the top down, and the firewall stops comparing the packet to the firewall rules when it finds a match. In other words, if you put an "allow any" rule as the first term in your match rules, then put specific "deny ..." rules below, all packets will match against the "allow any" and no packet will ever be compared against the "deny ..." rules;
  3. There is an implicit "deny all" at the very end of the firewall rules, even though it is not shown in the configuration. Therefore, you must always make sure that you include rules to match all of the traffic that you want to allow. For example, suppose you are seeing brute-force password guessing attacks through SSH from the IP address 1.2.3.4, so you create a rule to drop traffic matching the source address of 1.2.3.4 and destination port 22. Unless you configure a rule to allow everything else, the implicit "deny all" rule will drop all traffic coming in on the interface or VLAN to which you applied the firewall rule.


Let's start with a simple example: suppose I have a switch on the 192.168.1.0/24 network, I have a host at 192.168.1.1, and on my switch, I want to block ICMP messages from that host with an "ICMP Administratively Prohibited" notification, while allowing any other protocol from 192.168.1.1 or all traffic from other source addresses.

root@branch-3200# set firewall family inet filter no-icmp term drop-icmp from source-address 192.168.1.1

[edit]
root@branch-3200# set firewall family inet filter no-icmp term drop-icmp from protocol icmp

[edit]
root@branch-3200# set firewall family inet filter no-icmp term drop-icmp then reject administratively-prohibited

[edit]
root@branch-3200# commit
commit complete

[edit]
root@branch-3200#


If we attempt to ping 192.168.1.2 (the EX3200 switch) from 192.168.1.1 at this point, we will find the firewall isn't particularly effective:

root@main3200> ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2): 56 data bytes
64 bytes from 192.168.1.2: icmp_seq=0 ttl=64 time=3.630 ms
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=2.408 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=2.395 ms
^C
--- 192.168.1.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 2.395/2.811/3.630/0.579 ms

root@main3200>


Since we haven't applied the firewall to an interface or VLAN yet, no incoming packets are being compared against the firewall rules, and therefore all inbound traffic is still allowed. Let's take care of that, now:

root@branch-3200# set interfaces vlan unit 0 family inet filter input no-icmp

[edit]
root@branch-3200# commit
commit complete

[edit]
root@branch-3200#


To create our firewall, we started by adding a rule that drops traffic only if the source address is 192.168.1.1. However, we only want to drop ICMP traffic, so we then added a second rule (with the same term name, namely "drop-icmp") that drops ICMP traffic. Since the terms are cumulative, we've essentially created a rule that says, "if the source address matches 192.168.1.1 AND the protocol is ICMP..." Then, we created an action to reject matching packets with the "administratively prohibited" message. Let's see how it works:

root@main3200> ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2): 56 data bytes
36 bytes from 192.168.1.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
4  5  00 0054 d4e0   0 0000  40  01 2275 192.168.1.1  192.168.1.2
<...snip...>
^C
--- 192.168.1.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
root@main3200>


Good, good...

root@main3200> telnet 192.168.1.2
Trying 192.168.1.2...
telnet: connect to address 192.168.1.2: Operation timed out
telnet: Unable to connect to remote host

root@main3200>


Hrmmm...Well, we're half-way there. We are successfully blocking ICMP, but apparently, we are also blocking telnet. Remember, JunOS adds an implicit "deny all" at the end of the firewall rules. Since we create a rule to block ICMP from 192.168.1.1, but we didn't include a rule to allow any packets, we are actually blocking ALL packets on vlan.0. To fix this, we have to add a new term to allow anything that isn't ICMP and from 192.168.1.1:

root@branch-3200# set firewall family inet filter no-icmp term everything-else source 0.0.0.0/0

[edit]
root@branch-3200# set firewall family inet filter no-icmp term everything-else then accept

[edit]
root@branch-3200# commit
commit complete

[edit]
root@branch-3200#


Let's see if that solves the problem:

root@main3200> ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2): 56 data bytes
36 bytes from 192.168.1.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
4  5  00 0054 2f4c   0 0000  40  01 c809 192.168.1.1  192.168.1.2

36 bytes from 192.168.1.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
4  5  00 0054 2f76   0 0000  40  01 c7df 192.168.1.1  192.168.1.2

36 bytes from 192.168.1.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
4  5  00 0054 2f9c   0 0000  40  01 c7b9 192.168.1.1  192.168.1.2

^C
--- 192.168.1.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

root@main3200> telnet 192.168.1.2
Trying 192.168.1.2...
Connected to 192.168.1.2.
Escape character is '^]'.

branch-3200 (ttyp0)

login:
telnet> quit
Connection closed.

root@main3200>


That's more better :) We are now successfully blocking ICMP from 192.168.1.1 while still allowing other protocols, such as TCP/23 (telnet) to the switch.

However, looking at the firewall rules, something doesn't seem to be right. We have two rules to block source-address 192.168.1.1 and protocol ICMP, but the allow rule only specifies a source-address. How does this rule work?

Keep in mind that JunOS firewall rules work from top down, and they stop looking for matches as soon as a packet matches a term. Consequently, any packet originating from 192.168.1.1 and using the ICMP protocol matches our "reject ..." rule. However, if the packet comes from another source address or is a protocol other than ICMP, JunOS continues comparing the packet with the firewall rules. Therefore, anything not matching the "drop-icmp" term is compared to the "everything-else" term. The "everything-else" term tries to match the packet against the source-address of 0.0.0.0/0 -- in other words, against all possible source IP addresses. Since this second term will match a packet coming from any IP address, it is essentially an "allow any" rule. Therefore, our rule effectively reads, "if the packet matches 192.168.1.1 and matches the ICMP protocol, then reject it with the 'administratively-prohibited' message. If it is anything else, allow it."

Monday, November 11, 2013

JNCIA Lesson 4 -- Routing Policies and Firewall Filters

There's a particularly corny scene in the movie "Firewall" where Harrison Ford's character takes over for a junior network admin when the bank he works for is being DoS'd -- or maybe it was just a brute-force password guessing attack, I can't remember now -- by an outside attacker. Peering intently at the screen, Ford thinks for a minute, then pecks out a sequence of commands on the screen, finishing with a smug, "That should hold them up for a while," to which the junior admin replies, "It would hold me up!" What makes the scene corny is that they catch just a second of the monitor on-camera, and I could see that Ford (or more likely, their technical consultant) had typed a simple Cisco access control list to block traffic from a specific source IP address. Any competent network admin can tell you that a serious attacker could easily repeat the attack from another address, and another, and another, ad infinitum, until the increasingly complex access control list alone causes as much trouble as the initial attack.

However, ACL's -- or Juniper's even more flexible and powerful firewall filters -- have a valid role in network security, and on JunOS, they provide for an even more mundane -- yet essential -- role in configuring dynamic routing. Let me start with a simple example: suppose you have a router at your main office location, with two branch offices connected through a private network provided by your service provider (Metro-Ethernet service, for example). You have multiple VLANs on each router which must be able to communicate with each VLAN on the other routers. The main office router has a single Internet connection to your upstream service provider. You want each of the branch office routers to be able to connect to the Internet through your main office router. When finished, the network should look something like this:



In the last lesson, we configured a simple network using OSPF to route between three LAN networks. This time, however, we have added a fourth route to our ISP. Generally, a small or even medium sized business doesn't share routes with their ISP. The ISP doesn't care about routes to your internal network (that's why RFC-1918 was created in the first place), nor does a typical small office router have enough memory or processing power to store the entire Internet's routing table (especially not if using RIP or OSPF, but that's a whole other topic we won't get into now). In fact, if you only have a single connection to your ISP, this is a perfect use for a static route. Router A, which is the only router connected to the upstream service provider, will have a static, default route. If only there were a way to share that route with routers B and C...

On a Cisco router, this would be a piece of cake: you would simply create the default route, then use the "default-information originate" command to tell OSPF to distribute the default route in your OSPF area. On a Juniper, it is (IMHO) more complicated than it needs to be, but it's not really THAT bad. First, just like a Cisco, we'll create the default route:

root@main3200# top

[edit]
root@main3200# set routing-options static route 0.0.0.0/0 next-hop 100.64.170.65

[edit]
root@main3200#


At this point, you should be able to ping onto the Internet (or to a test network beyond the "ISP" router) from Router A. However, from Router B or Router C, there is still no default route defined:

root@branch2-3200# run show route 172.16.0.1

[edit protocols ospf]
root@branch2-3200#


Next, we have to define a routing policy to redistribute this route into OSPF...:

root@main3200# set policy-options policy-statement ospf-default term 1 from route-filter 0.0.0.0/0 exact accept

[edit]
root@main3200#


...and then export that filter into OSPF:

root@main3200# set protocols ospf export ospf-default

[edit]
root@main3200# commit
commit complete

[edit]
root@main3200#


If you try again from Router C...:

root@branch2-3200# run show route 172.16.0.1

inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[OSPF/150] 00:01:16, metric 0, tag 0
                    > to 10.11.12.1 via ge-0/0/21.0

[edit protocols ospf]
root@branch2-3200#


Success!!! However, if you're like I was when I first figured this out, you are probably going, "That's the stupidest, most convoluted thing..."

Yeah, I get it, and I don't entirely disagree. Cisco and pretty much everyone else who makes the network gear that I've played with in the past assume that you *want* to share routes (that's what a router is for, after all, right?), and so their default policy is exactly that: when you enable a dynamic routing protocol, it's going to start advertising all the routes it can find, or at the very least, there will be switches ("default-information originate," "redistribute connected," "redistribute static," etc.) that turn on very broad swathes of networks with a single command. However, in such cases, it can be very easy to share more information than you intended, and in fact, can be rather difficult to filter out some of the networks that you don't want to share. JunOS, on the other hand, takes the exact opposite approach, and only shares routes that you explicitly tell the router that you want to share. From a security standpoint, I'd have to say the Juniper approach might -- just maybe -- be the better choice. And honestly, their filtering language is much more flexible and powerful than Cisco's ACL's, IMHO. So, enough with the sales pitch; let's dissect those commands and try to figure out exactly what they mean.

The first command we used was "set routing-options static route 0.0.0.0/0 next-hop 100.64.170.65" which is essentially telling the router, "if you don't know what route to take to reach a destination address, just send it to our default gateway at 100.64.170.65."

The last command was "set protocols ospf export ospf-default" which was telling the router that we want to export the routes defined in a route filter that we named "ospf-default" into our OSPF routing protocol. Again, pretty simple and straightforward.

The magic all happens in the second command, "set policy-options policy-statement ospf-default term 1 from route-filter 0.0.0.0/0 exact accept." There's a lot happening in this command, so let's dissect it a little further. In the first half, "set policy-options policy-statement ospf-default..." we are telling the router that we are going to create a filter, and that we want to name the filter "ospf-default." Next, we define the filter. A filter in JunOS can be a sequence of match-action statements, which is roughly analogous to "if-then" statements in a programming language. You can chain multiple statements together in JunOS to create complex, compound statements, just like you would in a programming language. We want to match basically any destination address, so the pseudo-code for what we want to accomplish looks more or less like this:

if destination-address matches 0.0.0.0/0 then
  forward the packet through the static route that matches the destination 0.0.0.0/0


Okay, that seems easy enough. What if we want to route anything matching 172.16.10.0/24 to one router, and route anything else to a second router? Let's try it. First, we'll configure a physical interface through which we will forward traffic:

root@main3200# set interfaces ge-0/0/19 unit 0 family ethernet-switching

[edit]
root@main3200# set interfaces ge-0/0/19 unit 0 family inet address 100.64.5.2/26

[edit]
root@main3200# show interfaces ge-0/0/19
unit 0 {
    family inet {
        address 100.64.5.2/26;
    }
}

[edit]
root@main3200# commit
commit complete

[edit]
root@main3200#


I have connected a router to ge-0/0/19 on Router A to simulate another remote network (172.16.10.0/24) and connected a remote host on the outside interface of this router, using the IP address 172.16.10.20. On the outside interface of our "Internet" router, connected to ge-0/0/20, I have connected a host using the IP address 172.16.20.237. Connected to branch1-3200, I have a host using the IP address 192.168.2.32, and connected to branch1-3200, I have a host using the IP address 192.168.3.32. Right now, the two 192.168.x.32 hosts can ping each other, and the 172.16.20.237 host:

root@branch2-host1:~# ip addr | egrep -A3 eth0 | egrep "(eth0|inet) "
    inet 192.168.3.32/24 brd 192.168.3.255 scope global eth0
    inet 172.16.0.58/30 brd 172.16.0.59 scope global eth0.21
root@branch2-host1:~# ping -c1 192.168.2.32
PING 192.168.2.32 (192.168.2.32) 56(84) bytes of data.
64 bytes from 192.168.2.32: icmp_req=1 ttl=62 time=5.16 ms

--- 192.168.2.32 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.163/5.163/5.163/0.000 ms
root@branch2-host1:~# ping -c1 172.16.20.237
PING 172.16.20.237 (172.16.20.237) 56(84) bytes of data.
64 bytes from 172.16.20.237: icmp_req=1 ttl=61 time=1.34 ms

--- 172.16.20.237 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.342/1.342/1.342/0.000 ms
root@branch2-host1:~#


...and...:

me@branch1-host1:~$ ip addr | egrep eth0 | egrep "(eth0|inet) "
    inet 192.168.2.32/24 brd 192.168.2.255 scope global eth0
me@branch1-host1:~$ ping -c1 192.168.3.32
PING 192.168.3.32 (192.168.3.32) 56(84) bytes of data.
64 bytes from 192.168.3.32: icmp_req=1 ttl=62 time=2.93 ms

--- 192.168.3.32 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.936/2.936/2.936/0.000 ms
me@branch1-host1:~$ ping -c1 172.16.20.237
PING 172.16.20.237 (172.16.20.237) 56(84) bytes of data.
64 bytes from 172.16.20.237: icmp_req=1 ttl=61 time=1.51 ms

--- 172.16.20.237 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.510/1.510/1.510/0.000 ms
me@branch1-host1:~$


However, as of yet, there is no path to the host at 172.16.10.20:

root@branch2-host1:~# ping -c1 172.16.10.20
PING 172.16.10.20 (172.16.10.20) 56(84) bytes of data.
From 100.64.170.79 icmp_seq=1 Time to live exceeded

--- 172.16.10.20 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

root@branch2-host1:~#


...and...:

me@branch1-host1:~$ ping -c1 172.16.10.20
PING 172.16.10.20 (172.16.10.20) 56(84) bytes of data.
From 100.64.170.79 icmp_seq=1 Time to live exceeded

--- 172.16.10.20 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

me@branch1-host1:~$


This is expected, since we currently have a default route, redistributed through OSPF, from the main office router to both branch office routers. We now need to create a second route on the main office router to match 172.16.10.0/24, and redistribute it through OSPF to the branch office routers:

root@main3200# set routing-options static route 172.16.10.0/24 next-hop 100.64.5.1

[edit]
root@main3200# set policy-options policy-statement ospf-default term 2 from route-filter 172.16.10.0/24 exact accept

[edit]
root@main3200# set protocols ospf export ospf-default

[edit]
root@main3200#


We also need to tell the two branch routers how to reach the next-hop address of 100.64.5.1:

root@main3200# set protocols ospf area 0.0.0.0 interface ge-0/0/19.0 passive

[edit]
root@main3200# commit
commit complete

[edit]
root@main3200#


...and now we have both a default route through the "ISP" through ge-0/0/20 and to the host on 172.16.10.20 through ge-0/0/19, shared with all three office locations.

Saturday, October 5, 2013

Lesson 13 -- MAC Filtering and Port Security

In the wake of the virus incident, you do a little searching on-line and in your Cisco books, and decide perhaps it is time to implement another layer of security on your network. Cisco switches allow the network admin to configure ports to allow no more than a specified number of MAC addresses before taking some action, which on newer switches can be anything from simply dropping offending packets to logging the offence to shutting down the affected switch port entirely. Unfortunately, your 2924 switch only allows a subset of those actions, namely shutting down the port or simply sending an SNMP trap for the security violation. Still peeved that someone connected a personal laptop to your network -- in violation of example.com's corporate compliance policy -- you send out an e-mail to all employees reminding them that example.com does not yet have a BYOD policy, and that company policies currently prohibit connecting personal devices to the corporate network. Then, deciding to risk possibly becoming a bit of a BOfH, you implement port security on the 2924 switches, limiting switch ports to one MAC address per access (untagged VLAN) port, and automatically shutting down ports when a security violation is encountered.

lab2924a# conf t
lab2924a(config)# int fa0/7
lab2924a(config-if)# port security
lab2924a(config-if)# port security max-mac-count 1
lab2924a(config-if)# port security action shutdown
lab2924a(config)# int fa0/8
lab2924a(config-if)# port security
lab2924a(config-if)# port security max-mac-count 1
lab2924a(config-if)# port security action shutdown
lab2924a(config)# int fa0/9
lab2924a(config-if)# port security
lab2924a(config-if)# port security max-mac-count 1
lab2924a(config-if)# port security action shutdown
lab2924a(config)# int fa0/10
lab2924a(config-if)# port security
lab2924a(config-if)# port security max-mac-count 1
lab2924a(config-if)# port security action shutdown
<...snip...you get the idea...>


...and then repeat the config on lab2924b. To make sure everything is working as expected, you go to the server room and disconnect the cable from your port on the switch to the patch panel, then connect your laptop directly into the port. After getting a DHCP address from the server, you disconnect your laptop and connect a second laptop to the same port, and notice the link light briefly turns orange as spanning-tree reconverges, then goes out. You connect your laptop to another unused port, renegotiate an IP address with the DHCP server, then log in to the switch. You notice that the switchport to which the second laptop is still connected shows that it is "administratively down":

lab2924a# sho int fa0/9
FastEthernet0/9 is administratively down, line protocol is down
<...snip...>


You double-check the running configuration in RAM, to make sure that you plugged the second laptop into the correct port, and it shows the port is configured up -- looks like port security is working properly! To reset the port, you run "shut" on fa0/9, turn off port security, then run "no shut" and watch as the port comes up again (after a minute or so delay while spanning tree reconverges again). Since you have a spare laptop plugged into fa0/9, and that's your network port, you disconnect the second laptop, replace the cable in the patch panel, then return to your office before re-enabling port security on fa0/9.

Note: By this point, I'm starting to think that it might have been wise to spend a little more on a more modern switch for my CCNA practice. Once again, the commands I need to practice for the current CCNA exam are not supported on the switch I purchased (sigh...). On a modern switch, the procedure (in theory) goes like this:

switch(config)# int fa0/9
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security mac-address 00:15:c5:08:5d:92


...to statically assign a MAC address to the port security feature,...:

switch(config)# int fa0/9
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security mac-address sticky


...to have the switch automatically use the first MAC address it sees on the port, or...:

switch(config)# int fa0/9
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security maximum 5


...to automatically use the first five MAC addresses the switch learns from a given port. Next, you can select from three possible actions when the switch detects a violation. I'll show examples of a port that simply discards offending traffic (fa0/9), that discards and logs offending traffic (fa0/10), and that shuts down a port entirely (fa0/11):

switch(config)# int fa0/9
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security mac-address sticky
switch(config-if)# switchport port-security violations protect
switch(config)# int fa0/10
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security mac-address sticky
switch(config-if)# switchport port-security violations restrict
switch(config)# int fa0/11
switch(config-if)# switchport port-security
switch(config-if)# switchport port-security mac-address sticky
switch(config-if)# switchport port-security violations shutdown


Also, on newer switches, you supposedly only have to shut/no shut the interface to clear an automatic "shutdown" on a port when a violation occurs. On my 2924, a shut/no shut didn't release the port.

Maybe with all the vast wealth that I should receive from prospective employers after earning my CCNA (yeah, right...) I'll spring for a newer switch before starting in on the CCNP ;)