GRE over IPsec and Static NAT

Introduction

I have recently been studying IPsec based VPNs and kept running into the same struggle – Many sources out there claim that when implementing GRE over IPsec, if the tunnel traffic passes through a NAT device, then you must implement tunnel mode.  Notably, http://www.cisco.com/c/en/us/td/docs/solutions/Enterprise/WAN_and_MAN/P2P_GRE_IPSec/P2P_GRE/2_p2pGRE_Phase2.html#wp116954.

 

If the crypto tunnel transits either a Network Address Translation (NAT) or Port Address Translation (PAT) device, tunnel mode is required

 

This implies that transport mode is not compatible with GRE over IPsec going through a NAT device.  I thought fairly deeply about the problem for a while, and that didn’t make any sense, so I decided to lab it up.  I learned a lot, and what you find here might surprise you!  No matter the topic, when you are studying, never stop asking the questions why and how does that work.

Here is the lab topology we will be working on today:

 

Drawing1

R1 and R3 will be establishing a GRE over IPsec tunnel in all the examples that will follow.  Obviously, the tunnel traffic passes through R2.  R2 will be configured to static NAT R1 from 10.0.12.1 to 10.0.23.1.  Thus, the tunnel traffic between R1 and R3 clearly goes through a NAT device.  From R1’s perspective, the tunnel source is 10.0.12.1 and the tunnel destination is 10.0.23.3.  However, from R3’s perspective, the tunnel source is 10.0.23.3 and the tunnel destination is 10.0.23.1 (R1’s inside global address).

Note that we will run OSPF between all routers so that R1 and R3 will know how to get to the respective tunnel destination addresses. Once the tunnel interfaces come up, we will also run EIGRP over the tunnel to advertise the loopback addresses.

 

Initial Configurations

 

R1:

interface Loopback0
 ip address 1.1.1.1 255.255.255.255
!
interface GigabitEthernet1
 ip address 10.0.12.1 255.255.255.0
!
interface Tunnel0
 ip address 192.168.1.1 255.255.255.0
 tunnel source GigabitEthernet1
 tunnel destination 10.0.23.3
!
router eigrp 1
 network 1.1.1.1 0.0.0.0
 network 192.168.1.0
 passive-interface default
 no passive-interface Tunnel0
!
router ospf 1
 passive-interface default
 no passive-interface GigabitEthernet1
 network 10.0.12.0 0.0.0.255 area 0

R2:

interface GigabitEthernet1.100
 encapsulation dot1Q 100
 ip address 10.0.12.2 255.255.255.0
 ip nat inside
 ip ospf priority 255
!
interface GigabitEthernet1.101
 encapsulation dot1Q 101
 ip address 10.0.23.2 255.255.255.0
 ip nat outside
 ip ospf priority 255
!
ip nat inside source static 10.0.12.1 10.0.23.1
!
router ospf 1
 network 10.0.0.0 0.0.255.255 area 0
 default-information originate always

R3:

interface Loopback0
 ip address 3.3.3.3 255.255.255.255
!
interface GigabitEthernet1
 ip address 10.0.23.3 255.255.255.0
!
interface Tunnel0
 ip address 192.168.1.3 255.255.255.0
 tunnel source GigabitEthernet1
 tunnel destination 10.0.23.1
!
router eigrp 1
 network 3.3.3.3 0.0.0.0
 network 192.168.1.0
 passive-interface default
 no passive-interface Tunnel0
!
router ospf 1
 passive-interface default
 no passive-interface GigabitEthernet1
 network 10.0.23.0 0.0.0.255 area 0

Transport Mode Using Crypto Maps

Let’s put this to the test then.  If the article I linked to in the introduction is correct, this shouldn’t be possible, right?

R1:

crypto isakmp policy 10
 encr 3des
 hash md5
 authentication pre-share
!
crypto isakmp key cisco address 0.0.0.0
crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac
 mode transport
!
ip access-list extended R1-R3
 permit gre host 10.0.12.1 host 10.0.23.3
!
crypto map CRYPTO 10 ipsec-isakmp
 set peer 10.0.23.3
 set transform-set ESP-3DES-MD5
 match address R1-R3
!
interface GigabitEthernet1
 crypto map CRYPTO

R3:

crypto isakmp policy 10
 encr 3des
 hash md5
 authentication pre-share
!
crypto isakmp key cisco address 0.0.0.0
crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac
 mode transport
!
ip access-list extended R1-R3
 permit gre host 10.0.23.3 host 10.0.23.1
!
crypto map CRYPTO 10 ipsec-isakmp
 set peer 10.0.23.1
 set transform-set ESP-3DES-MD5
 match address R1-R3
!
interface GigabitEthernet1
 crypto map CRYPTO

Notice how the crypto map peer on R3 is pointing to the mapped address of R1…Now, does it work?

R1:

We can see our EIGRP adjacency over the tunnel came up, which is a great sign to start with…

R1#sh ip eigrp neigh
EIGRP-IPv4 Neighbors for AS(1)
H   Address                 Interface              Hold Uptime   SRTT   RTO  Q  Seq
                                                   (sec)         (ms)       Cnt Num
0   192.168.1.3             Tu0                      14 00:00:37    5  1470  0  30

Do we have R3’s loopback route learned over the tunnel?

R1#sh ip route eigrp | b Gateway
Gateway of last resort is 10.0.12.2 to network 0.0.0.0

      3.0.0.0/32 is subnetted, 1 subnets
D        3.3.3.3 [90/27008000] via 192.168.1.3, 00:02:09, Tunnel0

Finally, can we ping?

R1#ping 3.3.3.3 so lo0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 1.1.1.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 4/5/7 ms

Some IPsec verifications…

Phase 1 was fine

R1#sh crypto isakmp sa
IPv4 Crypto ISAKMP SA
dst             src             state          conn-id status
10.0.12.1       10.0.23.3       QM_IDLE           1007 ACTIVE

Phase 2 is fine…Notice that NAT-T is being used automagically, as it is detected in IKEv1 main mode.

R1#sh crypto ipsec sa

interface: GigabitEthernet1
    Crypto map tag: CRYPTO, local addr 10.0.12.1

   protected vrf: (none)
   local  ident (addr/mask/prot/port): (10.0.12.1/255.255.255.255/47/0)
   remote ident (addr/mask/prot/port): (10.0.23.3/255.255.255.255/47/0)
   current_peer 10.0.23.3 port 4500
     PERMIT, flags={origin_is_acl,}
    #pkts encaps: 10, #pkts encrypt: 10, #pkts digest: 10
    #pkts decaps: 11, #pkts decrypt: 11, #pkts verify: 11
    #pkts compressed: 0, #pkts decompressed: 0
    #pkts not compressed: 0, #pkts compr. failed: 0
    #pkts not decompressed: 0, #pkts decompress failed: 0
    #send errors 0, #recv errors 0

     local crypto endpt.: 10.0.12.1, remote crypto endpt.: 10.0.23.3
     plaintext mtu 1458, path mtu 1500, ip mtu 1500, ip mtu idb GigabitEthernet1
     current outbound spi: 0x57C8D38A(1472779146)
     PFS (Y/N): N, DH group: none

     inbound esp sas:
      spi: 0xE987C51D(3917989149)
        transform: esp-3des esp-md5-hmac ,
        in use settings ={Transport UDP-Encaps, }
        conn id: 2041, flow_id: CSR:41, sibling_flags FFFFFFFF80004008, crypto map: CRYPTO
        sa timing: remaining key lifetime (k/sec): (4607998/3580)
        IV size: 8 bytes
        replay detection support: Y
        Status: ACTIVE(ACTIVE)

     inbound ah sas:

     inbound pcp sas:

     outbound esp sas:
      spi: 0x57C8D38A(1472779146)
        transform: esp-3des esp-md5-hmac ,
        in use settings ={Transport UDP-Encaps, }
        conn id: 2042, flow_id: CSR:42, sibling_flags FFFFFFFF80004008, crypto map: CRYPTO
        sa timing: remaining key lifetime (k/sec): (4607999/3580)
        IV size: 8 bytes
        replay detection support: Y
        Status: ACTIVE(ACTIVE)

     outbound ah sas:

     outbound pcp sas:

So, myth busted – GRE over IPsec through a static NAT works just fine.  This makes sense when you think about the packet structure going on here.  Here is what things look like when R1 sends pings to R3:

[Outside IP][UDP][ESP][GRE][Inside IP][ICMP]

The inside IP header in this case is 1.1.1.1 –> 3.3.3.3.  The outside IP header is the header tacked on during GRE encapsulation. Because we are working with Transport mode, it goes outside the ESP encapsulation and looks like this: 10.0.12.1 –> 10.0.23.3.  When R3 receives the packet, it decrypts it, and finds a GRE packet inside. No worries.

Now, what is particular interesting about this solution is if you look at the IPsec SA from the perspective of R3…

R3:

R3#sh crypto ipsec sa | i local  ident|remote ident
   local  ident (addr/mask/prot/port): (10.0.23.3/255.255.255.255/47/0)
   remote ident (addr/mask/prot/port): (10.0.23.1/255.255.255.255/47/0)

Notice that the proxy identity negotiated from R1’s point of view is GRE (IP protocol 47) between 10.0.12.1 and 10.0.23.3, while from R3’s point of view, it is between 10.0.23.3 and 10.0.23.1, R1’s mapped address. So, the proxy identities in this case are not mirrored!

 

Tunnel Mode Using Crypto Maps

OK, we have seen that using transport mode is fine.  Let’s try tunnel mode.  After all, according to the Cisco article and other books I have read, this should work just fine…in fact, it is the “preferred” method, right? Before I made the below changes, I shut down the tunnel interfaces on both sides, and cleared the crypto and isakmp SAs on both sides

R1 and R3:

crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac
mode tunnel

Now, we will bring the tunnel interfaces back up on both sides and see what happens. Here is some output from R1. As you can see, phase 1 completed fine, but the EIGRP neighbor never comes up. Also, Phase 2 does not look good…

R1#sh ip int bri | i Tunnel
Tunnel0                192.168.1.1     YES NVRAM  up                    up

R1#sh crypto isakmp sa
IPv4 Crypto ISAKMP SA
dst             src             state          conn-id status
10.0.12.1       10.0.23.3       QM_IDLE           1008 ACTIVE

R1#sh ip eigrp neigh
EIGRP-IPv4 Neighbors for AS(1)

R1#sh crypto ipsec sa

interface: GigabitEthernet1
Crypto map tag: CRYPTO, local addr 10.0.12.1

protected vrf: (none)
local  ident (addr/mask/prot/port): (10.0.12.1/255.255.255.255/47/0)
remote ident (addr/mask/prot/port): (10.0.23.3/255.255.255.255/47/0)
current_peer 10.0.23.3 port 500
PERMIT, flags={origin_is_acl,ipsec_sa_request_sent}
#pkts encaps: 0, #pkts encrypt: 0, #pkts digest: 0
#pkts decaps: 1, #pkts decrypt: 1, #pkts verify: 1

Why? Let’s debug

R1#debug crypto ipsec
Crypto IPSEC debugging is on

*Apr  2 01:53:27.641: IPSEC(validate_proposal_request): proposal part #1
*Apr  2 01:53:27.641: IPSEC(validate_proposal_request): proposal part #1,
(key eng. msg.) INBOUND local= 10.0.12.1:0, remote= 10.0.23.3:0,
local_proxy= 10.0.23.1/255.255.255.255/47/0,
remote_proxy= 10.0.23.3/255.255.255.255/47/0,
protocol= ESP, transform= esp-3des esp-md5-hmac  (Tunnel-UDP),
lifedur= 0s and 0kb,
spi= 0x0(0), conn_id= 0, keysize= 0, flags= 0x0
*Apr  2 01:53:27.641: Crypto mapdb : proxy
R1#_match
src addr     : 10.0.23.1
dst addr     : 10.0.23.3
protocol     : 47
src port     : 0
dst port     : 0
*Apr  2 01:53:27.641: Crypto mapdb : proxy_match
src addr     : 10.0.23.1
dst addr     : 10.0.23.3
protocol     : 47
src port     : 0
dst port     : 0
*Apr  2 01:53:27.641: map_db_find_best did not find matching map
*Apr  2 01:53:27.641: IPSEC(ipsec_process_proposal): proxy identities not supported

OK…so what is this telling us?  R3 is sending us a proposal for the proxy identities here (what traffic should be encrypted).  R3 is proposing that GRE traffic between 10.0.23.1 (R1’s mapped address) and 10.0.23.3 be encrypted.  However, that doesn’t match what R1 thinks.  R1 think it should be between 10.0.12.1 (R1’s real address) and 10.0.23.3.  Now, despite that sounding pretty crappy, we just saw how non-mirrored proxy identities appeared to be just fine with transport mode, so what changed here?  Why is it OK in transport mode but not in tunnel mode?  The answer is in the headers.  Let’s say the routers did allow this to happen.  What would ping traffic look like between R3 and R1 from a header / data plane perspective when the packets reach R1?  Like this:

[Outside IP][UDP][ESP][GRE IP][GRE][Inside IP][ICMP] where…

Outside IP: 10.0.23.3 –> 10.0.12.1 (this is after R2 NATs the destination from 10.0.23.1 back to 10.0.12.1 in transit)

GRE IP: 10.0.23.3 –> 10.0.23.1 – This is R3’s perspective when the packet leaves R3.  However, remember this is encrypted in transit and encrypted when it arrives at R1…

Inside IP: 3.3.3.3 –> 1.1.1.1

Here is where things would break down.  Say R1 received the packet.  It decrypts the packet and sees inside the packet an IP header (The GRE IP header) destined to 10.0.23.1 (R1’s mapped address).  R1 has no idea what that is, so it throws the packet away. Tunnel mode using GRE/IPsec through a NAT device: Epic. Fail. Basically what is happening here is the routers are protecting us from ourselves.  The routers know that if these proposals were accepted, the data plane would break down when using tunnel mode.  So, instead of allowing that to happen, we get proxy identities are unsupported, and thus the phase II never completes properly.

OK, now could we get “creative” in a situation where for some reason we HAD to run tunnel mode in this situation? We can!  As we saw above, the root cause of this break down is the fact that in the encrypted payload we have an IP header with a destination of the mapped address of R1.  Since R1 has no idea what this is, it can’t process the packet.  So, what if R1 DID have an idea of what 10.0.23.1 was?  What if it existed on R1 itself?  Enter the loopback address hack. What we will do is add a loopback interface on R1 that matches R1’s mapped address.  That way, when the packets from R3 arrive and are decrypted, R1 will have a way to process the GRE packets sitting inside.

R1:

interface Loopback100 
 ip address 10.0.23.1 255.255.255.255 !
!
ip access-list extended R1-R3
 no permit gre host 10.0.12.1 host 10.0.23.3
 permit gre host 10.0.23.1 host 10.0.23.3
! 
interface Tunnel0
 tunnel source Loopback100

Will it work? You bet…

R1#ping 3.3.3.3 so lo0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 1.1.1.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 5/5/5 ms
!
R1#sh crypto ipsec sa | i pkts encap|pkts decap
    #pkts encaps: 19, #pkts encrypt: 19, #pkts digest: 19
    #pkts decaps: 40, #pkts decrypt: 40, #pkts verify: 40

Transport Mode with Tunnel Interfaces

OK, so far we have seen that at least with a crypto map implementation of GRE/IPsec through a NAT device, transport mode works just fine out of the box, while tunnel mode doesn’t work at all.  We also saw a clever method we could use to force tunnel mode into submission.  In theory, this should all work exactly the same with if we get rid of the crypto maps and instead implement GRE over IPsec using tunnel interfaces and IPsec profiles instead.  Let’s try, starting with transport mode again.  Note that before moving on here, I have shut down Lo100 on R1, and cleared the SAs again, as well as shut down the tunnel interfaces on both sides

R1:

crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac
 mode transport
!
crypto ipsec profile IPSEC
 set transform-set ESP-3DES-MD5
!
interface G1
 no crypto map CRYPTO
!
interface tunnel0
 tunnel source G1
 tunnel protection ipsec profile IPSEC

R3:

crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac 
 mode transport 
! 
crypto ipsec profile IPSEC 
 set transform-set ESP-3DES-MD5 
! 
interface G1 
 no crypto map CRYPTO 
! 
interface tunnel0 
 tunnel source G1 
 tunnel protection ipsec profile IPSEC

OK, let’s try again. I will bring the tunnels back up and see what happens.  As you can see below, the tunnel came right up, EIGRP came up, and I was able to ping.  This works for the exact same reason transport mode worked with crypto map implementation, so we won’t go too deep here.

R1#ping 3.3.3.3 so lo0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 1.1.1.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 5/5/6 ms

Tunnel Mode with Tunnel Interfaces

 

Since an IPsec profile applied to a GRE/IPv4 tunnel interface is essentially the exact same thing as having a GRE/IPv4 tunnel interface with crypto maps configured on the physical interface to protect the GRE traffic between the tunnel endpoints, we will not see much change here.  If we simply change our transform-set to tunnel mode here without the loopback hack we employed earlier, it will fail for the exact same reason it did when we tried it with crypto maps.  Let’s make sure:

R1 and R3:

crypto ipsec transform-set ESP-3DES-MD5 esp-3des esp-md5-hmac
 mode tunnel

Shut down the tunnels, clear the SA’s and bring the tunnels back up…Again a debug crypto ipsec on R1 shows the following:

*Apr  2 02:29:42.251: IPSEC(validate_proposal_request): proposal part #1
*Apr  2 02:29:42.251: IPSEC(validate_proposal_request): proposal part #1,
  (key eng. msg.) INBOUND local= 10.0.12.1:0, remote= 10.0.23.3:0,
    local_proxy= 10.0.23.1/255.255.255.255/47/0,
    remote_proxy= 10.0.23.3/255.255.255.255/47/0,
    protocol= ESP, transform= esp-3des esp-md5-hmac  (Tunnel-UDP),
    lifedur= 0s and 0kb,
    spi= 0x0(0), conn_id= 0, keysize= 0, flags= 0x0
*Apr  2 02:29:42.251: map_db_find_best did
not find matching map
*Apr  2 02:29:42.251: IPSEC(ipsec_process_proposal): proxy identities not supported

OK, then by the same logic as before, what if we use the loopback hack? So, lets put Lo100 back into service, and alter the tunnel configuration on R1 so that it sources things from Lo100:

interface Loopback100
 ip address 10.0.23.1 255.255.255.255
 no shut
!
interface Tunnel0
 tunnel source lo100

Now, does it work? No! The EIGRP neighbor is never established this time. What could go wrong? Debug crypto ipsec on R1 this time reveals the following:

*Apr  2 02:37:37.095: IPSEC(validate_proposal_request): proposal part #1
*Apr  2 02:37:37.095: IPSEC(validate_proposal_request): proposal part #1,
  (key eng. msg.) INBOUND local= 10.0.12.1:0, remote= 10.0.23.3:0,
    local_proxy= 10.0.23.1/255.255.255.255/47/0,
    remote_proxy= 10.0.23.3/255.255.255.255/47/0,
    protocol= ESP, transform= esp-3des esp-md5-hmac  (Tunnel-UDP),
    lifedur= 0s and 0kb,
    spi= 0x0(0), conn_id= 0, keysize= 0, flags= 0x0
*Apr  2 02:37:37.095: IPSEC(ipsec_process_
proposal): invalid local address 10.0.12.1

*Apr  2 02:37:54.575: IPSEC(sa_request): ,
  (key eng. msg.) OUTBOUND local= 10.0.23.1:500, remote= 10.0.23.3:500,
    local_proxy= 10.0.23.1/255.255.255.255/47/0,
    remote_proxy= 10.0.23.3/255.255.255.255/47/0,
    protocol= ESP, transform= esp-3des esp-md5-hmac  (Tunnel),
    lifedur= 3600s and 4608000kb,
    spi= 0x0(0), conn_id= 0, keysize= 0, flags= 0x0

OK, this looks familiar.  This is saying that R3 sends us a proposal. From R3’s perspective it is sending the packet to 10.0.23.1.  R2 then NATs the destination to 10.0.12.1 and the packet arrives at R1.  That is the INBOUND local= 10.0.12.1, remote= 10.0.23.3 part.  So we are receiving a packet FROM 10.0.23.3 destined TO 10.0.12.1.  Inside that proposal, R3 is proposing that all GRE traffic between 10.0.23.1 and 10.0.23.3 be encrypted.  R1 is actually on board with that, as you can see in the OUTBOUND proposal.  This is because we are sourcing our tunnel interface on R1 from Lo100.  So, why is it broken?

It is broken because when R2 does the NAT it changes the destination address to 10.0.12.1, the IP on R1 G1.  For this to work with a tunnel interface, since we are sourcing things from a Loopback, the packets need to be ultimately destined to that loopback address, 10.0.23.1 on R1.

This is where things get fairly insane…

R1:

interface Loopback100
 ip nat inside
!
interface GigabitEthernet1
 ip nat outside
!
ip nat inside source static 10.0.23.1 10.0.12.1

So now, R3 sends the proposal, and it is from 10.0.23.3 –> 10.0.23.1. R2 NATs the packet and sends to R1. It is now 10.0.23.3 –> 10.0.12.1. The packet arrives on R1, and R1 NATs the destination again. Finally we have 10.0.23.3 –> 10.0.23.1 on R1, and away we go…

R1#ping 3.3.3.3 so lo0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 3.3.3.3, timeout is 2 seconds:
Packet sent with a source address of 1.1.1.1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 5/5/5 ms

Weeeeeeeeee

Summary

With regards to GRE over IPsec through a static NAT device, I can conclude the following based on my own lab testing:

  • Transport mode works fine with both crypto map and tunnel interface implementation
  • Tunnel mode is broken by default with both crypto map and tunnel interface implementation due to the way tunnel mode IPsec interacts with GRE tunnel interfaces
  • Tunnel mode can be made to work by using some hacks, although in the real world that would be a really really nasty way to do things.  In something like the CCIE lab…all bets are off.

Keep in mind, the purpose of this exercise was explicitly to test GRE over IPsec.  When you have other technologies involved, there are certainly other options.  For example, VTI interfaces, DMVPN, etc…the purpose of this lab was to test and validate GRE over IPsec L2L explicitly.

Thanks for playing!

9 Comments

Leave a Reply to Paul Lampron Cancel reply