According to the statistics, a few people have stumbled across this blog because they were searching for certain 7750-specific information relating to LACP. Here are a couple of answers that were missed out from my main LACP article:
What is the failover time for a LAG / etherchannel?
The answer to this question varies considerably depending on the setup. If a device notices a bundled interface going physically down then it should unbundle it immediately, causing very low loss (50ms should be achievable).
In the event of an interface remaining physically up (i.e. where there is transmission equipment or EoMPLS between the two devices), also known as a silent failure, the failover will be up to 3 times the LACP timer. So the impact would be up to 3 seconds using fast timers or up to 90 seconds using slow timers. Most lower end Cisco kit only supports slow timers.
In the event of a single fibre fault or other asymmetric failure, you may see a combination of these effects where traffic in one direction heals faster than the other. There are other corner cases such as when administratively shutting down an interface - some devices send an out of sync LACPDU to inform the other end the link is about to go away which helps speed convergence. It is really best to lab test where possible to check different failure scenarios.
Be aware that when using load balanced LAGs, the impact to some streams may be zero. Typically traffic that is hashed onto one link in the bundle will not suffer loss when a different link in the bundle fails.
How can I transport, rather than terminate, LACP through epipe services on the Alcatel-Lucent 7750?
The answer to this is pretty straightforward, but I know I looked in the wrong place when I first needed to use the feature.
All you need to do is to configure "lacp-tunnel" under the configure -> port -> ethernet context.
How can I transport, rather than terminate, LACP through a QinQ tunnel on a Cisco switch?
Again, this is pretty straightforward. There are a load of different protocols that can optionally be tunneled on a dot1q-tunnel port, but we just need lacp enabled:
Simply configure "l2protocol-tunnel point-to-point lacp" under the dot1q-tunnel interface.
Normally it makes sense to tunnel everything (STP, CDP, VTP, LLDP, LACP, PAgP, ...) for consistency. Either be a tunnel or don't!
What is the valid range for LAG IDs on the 7750?
For IOM-based systems (i.e. SR-7, SR-12), the usable LAG ID range is 1 to 200. For integrated IOM systems such as SR-1 and ESS-1, the LAG ID range is 1 to 64.
Can you tell me something unusual about LACP on the 7750?
When the Rx fibre for a LACP-speaking port loses light (i.e. fails), right before the port gets pulled down the 7750 sends an LACP out-of-sync message to inform the other end that it is going away. This is useful for single fibre faults and can drastically improve convergence times, particularly where transmission equipment between the two LACP peers does not forward link loss.
What does "mux: during state COLLECTING_DISTRIBUTING, got event 5(in_sync) (ignored)" mean in a Cisco debug?
As best I can tell it means that a LACPDU was received with the sync bit set, indicating that the far end is ready to use the link, but the link was already collecting / distributing (i.e. in use) so no change in state was required.
What does "lag number : partner oper state bits changed on member port : [expired false -> true]" mean on a 7750 debug?
This means that a particular port's state machine moved into the expired state due to missing three inbound LACPDUs from the peer. Once the port reaches the expired state it is removed from the bundle but the peer parameters are remembered for a further 3 intervals, after which point the peer information is flushed and the port enters the defaulted state.
Do the LACP keys need to match at both ends of a LAG?
No - the LACP key is locally significant and corresponds one-to-one with a LAG or etherchannel ID. It is used to check consistency (i.e. to catch crossed cables) so must be identical for all members within a LAG, however the devices at each end of the LAG can select any value they like for any particular LAG.
Why do ports get "suspended" from an etherchannel?
Basically a port gets suspended if its configuration is not in line with that of the port channel with which it is associated. The most common way to accidentally arrive in this state is for a member trunk port to have a different allowed VLAN list than its parent port-channel interface. While IOS allows member ports to be reconfigured, it is much more sensible to make the configuration changes to the port-channel interface - the changes are then pushed down to the member ports automatically, avoiding this kind of conflict.
Showing posts with label tunnel. Show all posts
Showing posts with label tunnel. Show all posts
Friday, 12 April 2013
Sunday, 18 November 2012
Simulating a broken LNS
A common requirement when testing a LAC is to confirm its reaction when various failure codes are returned by the LNS. In theory you would expect the LAC to react to an LNS failure in the same way (i.e. try another) irrespective of the error type or code returned, but as we all know theory and practice don't always align and that is why we test.
I recently had to prove exactly this area of functionality and found that, while it is relatively easy to put an LNS together which will terminate sessions, it's actually quite hard to get a real LNS to return error messages. Would you believe that they appear to be designed not to fail?
So the aim was:
The intended use case for this script is to have the LAC attempt to connect to an LNS which is "behind" the host running scapy, i.e. the last hop router should have a static route directing traffic for the LNS via the scapy host, in effect creating the following topology:
Alternatively, you could use a static ARP entry on the gateway router to direct traffic for an address on the attached LAN to the scapy host.
interface (default "eth1")
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.0.1)
>>> execfile('BrokenLNS.py')
>>> lns = LNS()
>>> lns.resultcode = 1
>>> lns.errorcode = 6
>>> lns.errormessage = "Oh, no!"
>>> lns.run()
Received L2TP packet from 1.2.3.4
Got an SCCRQ
Sending spoofed StopCCN from 172.16.0.20 to 1.2.3.4.
.
Sent 1 packets.
Received L2TP packet from 1.2.3.4
^C>>>
# Flags
MANDATORY = 32768
HIDDEN = 16384
CONTROL = 32768
L = 16384
S = 2048
# Types
CONTROLMESSAGE = 0
ERRORMESSAGE = 1
PROTOCOLVERSION = 2
HOSTNAME = 7
RECVWIN = 10
FRAMING = 3
BEARER = 4
FIRMWARE = 6
TUNNELID = 9
CHALLENGE = 11
# Control Message Types
SCCRQ = '\x00\x01'
SCCRP = '\x00\x02'
StopCCN = '\x00\x04'
def word(value):
# Generates a two byte representation of the provided number
return(chr((value/256)%256)+chr(value%256))
def AVP(bitmask, vendor, attribute_type, data):
# Generates an L2TP AVP using the given attribute number and payload
length = len(data) + 6
return(word(bitmask + (length % 1024)) + word(0) + word(attribute_type) + data)
def genL2TP(flags, tunid, sessid, ns, nr, payload):
# Generates an L2TP payload with the given parameters and AVP payload
length = len(payload) + 12
return(word(flags | 2) + word(length) + tunid + sessid + word(ns) + word(nr) + payload)
def getAVP(avp, payload):
loc = 0
while(loc < len(payload)):
avp_type = payload[loc+2:loc+6]
avp_len = ((ord(payload[loc:loc+1]) & 3) * 256) + ord(payload[loc+1:loc+2])
# Uncomment the following line if you want to see info on every AVP checked
# print "Got AVP " + str(ord(avp_type[0:1])).zfill(2) + str(ord(avp_type[1:2])).zfill(2) + str(ord(avp_type[2:3])).zfill(2) + str(ord(avp_type[3:4])).zfill(2) + " of length " + str(avp_len) + " value " + payload[loc+6:loc+avp_len]
if avp_type == avp:
return(payload[loc+6:loc+avp_len])
loc = loc + avp_len
class LNS(Automaton):
interface = "eth1"
resultcode = 0
errorcode = 4
errormessage = "Internal error"
# Define possible states
# Since this is so simple we only need one state :)
@ATMT.state(initial=1)
def WAIT(self):
pass
# Define transitions
# Transitions from WAIT
@ATMT.receive_condition(WAIT)
def receive_sccrq(self,pkt):
if (UDP in pkt) and pkt.dport==1701:
print "Received L2TP packet from " + pkt[IP].src
# scapy's built in L2TP handling doesn't deal well with control messages so
# we just grab the raw data from beyond the UDP header
payload = pkt[UDP].build_payload()
# Check what type of L2TP message arrived by chopping off the header and passing
# the rest to getAVP
packet_type = getAVP(word(0) + word(CONTROLMESSAGE), payload[12:])
if(packet_type == SCCRQ):
# If we get an SCCRQ, generate a StopCCN in response.
print "Got an SCCRQ"
client_ip = pkt[IP].src
server_ip = pkt[IP].dst
client_mac = pkt[Ether].src
server_mac = pkt[Ether].dst
tun_id = getAVP(word(0) + word(TUNNELID), payload[12:])
print "Sending spoofed StopCCN from " + server_ip + " to " + client_ip + "."
sendp(Ether(src=server_mac, dst=client_mac)/IP(src=server_ip, dst=client_ip)/UDP(sport=1701, dport=1701)/Raw(load=genL2TP(CONTROL | L | S, tun_id, word(0), 0, 1, AVP(MANDATORY, 0, CONTROLMESSAGE, StopCCN) + AVP(MANDATORY, 0, ERRORMESSAGE, word(self.resultcode) + word(self.errorcode) + self.errormessage) + AVP(MANDATORY, 0, TUNNELID, word(12345)))), iface=self.interface)
raise self.WAIT()
elif(packet_type == SCCRP):
print "is an SCCRP"
elif(packet_type == StopCCN):
print "is a StopCCN"
else:
print "is a ZLB or non-control message"
I recently had to prove exactly this area of functionality and found that, while it is relatively easy to put an LNS together which will terminate sessions, it's actually quite hard to get a real LNS to return error messages. Would you believe that they appear to be designed not to fail?
So the aim was:
- To have an 'LNS' which could be configured to reject incoming start control connection requests (SCCRQs)
- To be able to configure the result code, error code and, to make the packet captures easier to read and more authentic, the error message contained within the StopCCN message
- Ideally, to be able to service requests arriving on multiple IP addresses
Important:
The script shown below does exactly what I needed but doesn't exactly work how you might expect. In order to reduce reconfiguration between test cases I have made it respond to queries arriving on any IP address - it does this by inspecting the incoming SCCRQ's source and destination MAC and IP addresses, then flipping them around on the response. That means that it does not attempt to bind to port 1701 on the host, therefore if the LAC sends an SCCRQ to the host's real IP it will get an ICMP unreachable and a StopCCN back. This is almost certainly not what you want.The intended use case for this script is to have the LAC attempt to connect to an LNS which is "behind" the host running scapy, i.e. the last hop router should have a static route directing traffic for the LNS via the scapy host, in effect creating the following topology:
Alternatively, you could use a static ARP entry on the gateway router to direct traffic for an address on the attached LAN to the scapy host.
Usage
Usage is simple - firstly run scapy, then call 'execfile("BrokenLNS.py")' to load the script. You must create an instance of "LNS" and then, if the defaults to not suit, set the following member values:interface (default "eth1")
- resultcode (default 0)
- errorcode (default 4)
- errormessage (default "Internal error")
Example
root@scapyhost:~/Projects/BrokenLNS# scapyWARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.0.1)
>>> execfile('BrokenLNS.py')
>>> lns = LNS()
>>> lns.resultcode = 1
>>> lns.errorcode = 6
>>> lns.errormessage = "Oh, no!"
>>> lns.run()
Received L2TP packet from 1.2.3.4
Got an SCCRQ
Sending spoofed StopCCN from 172.16.0.20 to 1.2.3.4.
.
Sent 1 packets.
Received L2TP packet from 1.2.3.4
^C>>>
Code
import os# Flags
MANDATORY = 32768
HIDDEN = 16384
CONTROL = 32768
L = 16384
S = 2048
# Types
CONTROLMESSAGE = 0
ERRORMESSAGE = 1
PROTOCOLVERSION = 2
HOSTNAME = 7
RECVWIN = 10
FRAMING = 3
BEARER = 4
FIRMWARE = 6
TUNNELID = 9
CHALLENGE = 11
# Control Message Types
SCCRQ = '\x00\x01'
SCCRP = '\x00\x02'
StopCCN = '\x00\x04'
def word(value):
# Generates a two byte representation of the provided number
return(chr((value/256)%256)+chr(value%256))
def AVP(bitmask, vendor, attribute_type, data):
# Generates an L2TP AVP using the given attribute number and payload
length = len(data) + 6
return(word(bitmask + (length % 1024)) + word(0) + word(attribute_type) + data)
def genL2TP(flags, tunid, sessid, ns, nr, payload):
# Generates an L2TP payload with the given parameters and AVP payload
length = len(payload) + 12
return(word(flags | 2) + word(length) + tunid + sessid + word(ns) + word(nr) + payload)
def getAVP(avp, payload):
loc = 0
while(loc < len(payload)):
avp_type = payload[loc+2:loc+6]
avp_len = ((ord(payload[loc:loc+1]) & 3) * 256) + ord(payload[loc+1:loc+2])
# Uncomment the following line if you want to see info on every AVP checked
# print "Got AVP " + str(ord(avp_type[0:1])).zfill(2) + str(ord(avp_type[1:2])).zfill(2) + str(ord(avp_type[2:3])).zfill(2) + str(ord(avp_type[3:4])).zfill(2) + " of length " + str(avp_len) + " value " + payload[loc+6:loc+avp_len]
if avp_type == avp:
return(payload[loc+6:loc+avp_len])
loc = loc + avp_len
class LNS(Automaton):
interface = "eth1"
resultcode = 0
errorcode = 4
errormessage = "Internal error"
# Define possible states
# Since this is so simple we only need one state :)
@ATMT.state(initial=1)
def WAIT(self):
pass
# Define transitions
# Transitions from WAIT
@ATMT.receive_condition(WAIT)
def receive_sccrq(self,pkt):
if (UDP in pkt) and pkt.dport==1701:
print "Received L2TP packet from " + pkt[IP].src
# scapy's built in L2TP handling doesn't deal well with control messages so
# we just grab the raw data from beyond the UDP header
payload = pkt[UDP].build_payload()
# Check what type of L2TP message arrived by chopping off the header and passing
# the rest to getAVP
packet_type = getAVP(word(0) + word(CONTROLMESSAGE), payload[12:])
if(packet_type == SCCRQ):
# If we get an SCCRQ, generate a StopCCN in response.
print "Got an SCCRQ"
client_ip = pkt[IP].src
server_ip = pkt[IP].dst
client_mac = pkt[Ether].src
server_mac = pkt[Ether].dst
tun_id = getAVP(word(0) + word(TUNNELID), payload[12:])
print "Sending spoofed StopCCN from " + server_ip + " to " + client_ip + "."
sendp(Ether(src=server_mac, dst=client_mac)/IP(src=server_ip, dst=client_ip)/UDP(sport=1701, dport=1701)/Raw(load=genL2TP(CONTROL | L | S, tun_id, word(0), 0, 1, AVP(MANDATORY, 0, CONTROLMESSAGE, StopCCN) + AVP(MANDATORY, 0, ERRORMESSAGE, word(self.resultcode) + word(self.errorcode) + self.errormessage) + AVP(MANDATORY, 0, TUNNELID, word(12345)))), iface=self.interface)
raise self.WAIT()
elif(packet_type == SCCRP):
print "is an SCCRP"
elif(packet_type == StopCCN):
print "is a StopCCN"
else:
print "is a ZLB or non-control message"
Labels:
error code,
error message,
L2TP,
LAC,
LNS,
response code,
scapy,
SCCRQ,
spoof,
StopCCN,
tunnel
Subscribe to:
Posts (Atom)
