Junos OS DHCP in VRFs

As more users look to consolidate systems and networking resources in my current job, there has been an increasing need to use VRFs for specific application. This time, I’m not talking about VRFs in the MPLS sense, just “VRF lite” or virtual-rotuer in Junos. Fairly common practice on shared managed infrastructure such as firewalls or VPN concentrators is running DHCP services or bootp/dhcp forwarding inside virtual routers. Until now Junos really hasn’t had a way of doing this in a clean fashion unlike IOS which makes this process extremely simple. Before the release of 12.1 as far as I’m aware, there was no way of using the built in dhcp process to assign address/attributes to any network that wasn’t in the master routing instance. I managed to find a work around via a kb artcile for this which i’ll demonstrate below, and let me be clear, I am not even close to a fan of it.

**Note I use VRF and virtual-router interchangeably here**

Scenario: “guest” wireless network separate from all corporate resources using an SRX as a switch/firewall/router on a stick.

set security policies from-zone guest to-zone internet policy guest-internet-access match source-address 192.168.4.0/24
set security policies from-zone guest to-zone internet policy guest-internet-access match destination-address any
set security policies from-zone guest to-zone internet policy guest-internet-access match application any
set security policies from-zone guest to-zone internet policy guest-internet-access then permit

set vlans guest vlan-id 30
set vlans guest interface ge-0/0/15.0
set vlans guest l3-interface vlan.30

set interfaces vlan unit 30 description "Guest Internet access"
set interfaces vlan unit 30 family inet address 192.168.4.1/24

set routing-instances guest instance-type virtual-router
set routing-instances guest interface vlan.30
set routing-instances guest routing-options static route 0.0.0.0/0 next-hop *internet gateway*
set routing-instances guest routing-options instance-import inet.0-to-guest

set system services dhcp pool 192.168.1.0/24 address-range low 192.168.4.100
set system services dhcp pool 192.168.1.0/24 address-range high 192.168.4.200
set system services dhcp pool 192.168.1.0/24 name-server 8.8.8.8
set system services dhcp pool 192.168.1.0/24 router 192.168.4.1

set security zones security-zone guest address-book address 192.168.4.0/24 192.168.4.0/24
set security zones security-zone guest host-inbound-traffic system-services ping
set security zones security-zone guest host-inbound-traffic system-services traceroute
set security zones security-zone guest interfaces vlan.30 host-inbound-traffic system-services dhcp

This is a very basic security and interface configuration, however if you’ve ever played around with the systems services hierarchy, you’ll notice there ins’t a VRF option like there is in IOS, for example:

ip dhcp pool GUEST_DHCP
   vrf VPN
   network 192.168.2.0 255.255.255.0
   dns-server 202.37.101.1 202.37.101.2 
   default-router 192.168.2.1 
   class GUEST_DHCP
      address range 192.168.2.10 192.168.2.30

From this point onwards, I essentially have to kick into troubleshooting mode to see what the firewall is doing with the dhcp messages from clients wanting an address. Here I’ve setup some simple traceoptions to output to file.

perrin@srx_nz> show log dhcp-debug              
Jun 18 09:47:27 received packet from 0.0.0.0 port 68 interface vlan.30 routing instance guest
Jun 18 09:47:27 packet from 0.0.0.0 discarded: not default routing instance
Jun 18 09:47:28 received packet from 0.0.0.0 port 68 interface vlan.30 routing instance guest
Jun 18 09:47:28 packet from 0.0.0.0 discarded: not default routing instance
Jun 18 09:47:30 received packet from 0.0.0.0 port 68 interface vlan.30 routing instance guest

As you can see, the router is actually dropping the dhcp requests! Pre 12.1 I’m only aware of this one way to “fix” this thanks to the juniper KB articles. If your running 12.1 and on, stay with me, I have the proper solution next!

Juniper recommends getting around this problem by installing a firewall filter on the terminating interface that captures only DHCP traffic on UDP 67 and 68 and allows this into the master routing instance, then, places all other traffic into the guest vrf which we have configured. You also move the interface out of the VRF and into the master RI as well!!

set firewall family inet filter guest-dhcp term dhcp-to-inet.0 from protocol udp 
set firewall family inet filter guest-dhcp term dhcp-to-inet.0 from port 68
set firewall family inet filter guest-dhcp term dhcp-to-inet.0 from port 67
set firewall family inet filter guest-dhcp term dhcp-to-inet.0 then count dhcp-packet
set firewall family inet filter guest-dhcp term dhcp-to-inet.0 then accept 
set firewall family inet filter guest-dhcp term any then routing-instance test 
!
delete routing-instances guest interface vlan.30
!
set interfaces vlan unit 30 family inet filter input guest-dhcp

Because you have moved the interface out of the VRF and back into inet.0 but you are still placing traffic into the VRF with the firewall filter. You have to create a virtual-router import/export policy so the too tables can talk to each other. (I will do a full post on doing this inside virtual-routers in another video)

set routing-instances VVisitor routing-options instance-import inet.0-to-guest
!
set policy-options policy-statement inet.0-to-guest term 1 from protocol static
set policy-options policy-statement inet.0-to-guest term 1 from route-filter 0.0.0.0/0 exact
set policy-options policy-statement inet.0-to-guest term 1 then accept
set policy-options policy-statement inet.0-to-guest from instance master
set policy-options policy-statement inet.0-to-guest then reject
!
set routing-options static route 192.168.4.0/24 next-table guest.inet.0
!
perrin@srx_nz> show route 192.168.4.0/24   

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

192.168.4.0/24     *[Static/5] 5d 09:10:15
                      to table guest.inet.0


perrin@srx_nz> show route 0.0.0.0 table guest 

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

0.0.0.0/0          *[Static/5] 5d 09:10:44
                    > to *nexthop* via ge-0/0/1.0

With this complete, check the dhcp log file to see if dhcp traffic is being correctly processed by the interface.

Jun 18 12:06:12 Reading pools
Jun 18 12:06:12 Found a pool configuration for 192.168.4.0/24
Jun 18 12:06:12 -- looking for pool with subnet 192.168.4.0, prefix length 24, including shadowed
Jun 18 12:06:12 adding child `Pool' scope 0x57b000 to `Global' scope 0x576400
Jun 18 12:06:12 creating a new children entry for `Pool' type
Jun 18 12:06:12 added `Pool' child 0x57b000 to `Global' scope 0x576400
Jun 18 12:06:12 allocated new scope `Pool' type 2 size 1224
Jun 18 12:06:12 -- looking for pool with subnet 192.168.4.0, prefix length 24
Jun 18 12:06:12 added pool `192.168.4.0/24' with subnet 192.168.4.0
Jun 18 12:06:12 set range on pool `192.168.4.0/24' to 192.168.4.1, 192.168.4.254
Jun 18 12:06:12 set range on pool `192.168.4.0/24' to 192.168.4.100, 192.168.4.200
Jun 18 12:06:12 reading lease time options for pool
Jun 18 12:06:12 Maximum lease time infinite obtained from `Global' scope
Jun 18 12:06:12 Default lease time 1 day obtained from `Global' scope
Jun 18 12:06:12 reading common options for pool
Jun 18 12:06:12 propagate settings done - number of ifls in scope 0x57b000 are 0
Jun 18 12:06:12 reading name-server
Jun 18 12:06:12 reading router
Jun 18 12:06:12 Set 1 router address on `Pool' scope
Jun 18 12:06:12 Reading static bindings
Jun 18 12:06:12 Reading interface configuration
!
perrin@srx_nz# run show system services dhcp binding 
IP address       Hardware address   Type     Lease expires at
192.168.4.101    dc:9f:db:1a:e0:35  dynamic  2014-06-19 12:06:14 UTC
192.168.4.100    dc:9f:db:1a:e0:51  dynamic  2014-06-19 12:06:13 UTC

This indeed works but by no means is it pretty, by adding filters and removing the interface from the VRF, you introduce unnecessary complexity and solutions that aren’t very scalable. Fortunately as I mentioned earlier, after 12.1 there is now a fix for this using the “access address-assignment” command inside the routing-instance hierarchy.

set access address-assignment pool guest family inet network 192.168.4.0/24
set access address-assignment pool guest family inet range pool low 192.168.4.100
set access address-assignment pool guest family inet range pool high 192.168.4.200
set access address-assignment pool guest family inet dhcp-attributes maximum-lease-time 7200
set access address-assignment pool guest family inet dhcp-attributes name-server 8.8.8.8
set access address-assignment pool guest family inet dhcp-attributes router 192.168.4.1
!
set routing-instances guest system services dhcp-local-server group guest interface vlan.30

That last line is the new feature introduced from 12.1 (I’m using 12.1X46-D20.5) It just seems too easy but this is all you need to do to assign the address assignment pool to the virtual-router. To verify this configuration, you can again check the dhcp logs, otherwise you can do it the normal way by checking the server bindings. The way you’ve always checked it using “show dhcp server binding routing-instance” or you can check it using the network-access command

perrin@srx_nz> show network-access address-assignment pool guest routing-instance guest.

Cool! DHCP in VRFs in Junos OS! Cheers to Fraser McGlinn for throwing some questions back and forth with me to come up with this.