Add DHCP relay

Change-Id: I75aa18b6798824285de4cdc101917ed72d8149d6
diff --git a/trellis/bgpdbgp1.conf b/trellis/bgpdbgp1.conf
index 383460c..5400dfd 100644
--- a/trellis/bgpdbgp1.conf
+++ b/trellis/bgpdbgp1.conf
@@ -6,6 +6,7 @@
 !
 ip prefix-list 1 seq 10 permit 10.0.2.0/24
 ip prefix-list 1 seq 20 permit 10.0.3.0/24
+ip prefix-list 1 seq 30 permit 10.0.4.0/24
 !
 route-map NEXTHOP4 permit 10
 match ip address prefix-list 1
@@ -42,6 +43,7 @@
 !
 network 10.0.2.0/24
 network 10.0.3.0/24
+network 10.0.4.0/24
 !
 ! IPv6
 !
diff --git a/trellis/dhcpd.conf b/trellis/dhcpd.conf
index f5d338f..b193ce9 100644
--- a/trellis/dhcpd.conf
+++ b/trellis/dhcpd.conf
@@ -16,12 +16,17 @@
   option routers 10.0.3.254;
 }
 
+subnet 10.0.4.0 netmask 255.255.255.0 {
+  range 10.0.4.100 10.0.4.240;
+  option routers 10.0.4.254;
+}
+
 subnet 10.0.99.3 netmask 255.255.255.255 {
 }
 
 host h1 {
   hardware ethernet 00:aa:00:00:00:01;
-  fixed-address 10.0.2.1;
+  fixed-address 10.0.4.1;
 }
 
 host h2 {
diff --git a/trellis/trellis_remote_dhcp.py b/trellis/trellis_remote_dhcp.py
index 606935e..33a5974 100755
--- a/trellis/trellis_remote_dhcp.py
+++ b/trellis/trellis_remote_dhcp.py
@@ -11,7 +11,7 @@
 from ipaddress import ip_network
 from routinglib import BgpRouter
 from routinglib import RoutedHost
-from trellislib import DhcpClient, DhcpServer
+from trellislib import DhcpClient, DhcpServer, DhcpRelay
 
 class Trellis( Topo ):
     "Trellis basic topology"
@@ -33,6 +33,17 @@
         self.addLink(s227, s204)
         self.addLink(s227, s205)
 
+        intfs = {
+            'relay-eth0': {
+                'ipAddrs': ['10.0.4.254/24']
+            },
+            'relay-eth1': {
+                'ipAddrs': ['10.0.2.100/24']
+            }
+        }
+        dhcpRelay = self.addHost('relay', cls=DhcpRelay, serverIp='10.0.99.3',
+                                 gateway='10.0.2.254', interfaces=intfs)
+
         # NOTE avoid using 10.0.1.0/24 which is the default subnet of quaggas
         # NOTE avoid using 00:00:00:00:00:xx which is the default mac of host behind upstream router
         # IPv4 Hosts
@@ -40,7 +51,8 @@
         h2 = self.addHost('h2', cls=DhcpClient, mac='00:aa:00:00:00:02')
         h3 = self.addHost('h3', cls=DhcpClient, mac='00:aa:00:00:00:03')
         h4 = self.addHost('h4', cls=DhcpClient, mac='00:aa:00:00:00:04')
-        self.addLink(h1, s204)
+        self.addLink(h1, dhcpRelay)
+        self.addLink(dhcpRelay, s204)
         self.addLink(h2, s204)
         self.addLink(h3, s205)
         self.addLink(h4, s205)
diff --git a/trellis/trellislib.py b/trellis/trellislib.py
index db0fb65..11a57b0 100644
--- a/trellis/trellislib.py
+++ b/trellis/trellislib.py
@@ -7,7 +7,7 @@
 import sys
 sys.path.append('..')
 from mininet.node import Host
-from routinglib import RoutedHost
+from routinglib import RoutedHost, Router
 
 class TaggedRoutedHost(RoutedHost):
     """Host that can be configured with multiple IP addresses."""
@@ -95,6 +95,28 @@
         self.cmd('rm -rf  %s' % self.leasesFile)
         super(DhcpServer, self).terminate()
 
+class DhcpRelay(Router):
+    binFile = '/usr/sbin/dhcrelay'
+    pidFile = '/run/dhcp-relay.pid'
+    serverIp = None
+    gateway = None
+
+    def __init__(self, name, serverIp, gateway, *args, **kwargs):
+        super(DhcpRelay, self).__init__(name, **kwargs)
+        self.serverIp = serverIp
+        self.gateway = gateway
+
+    def config(self, **kwargs):
+        super(DhcpRelay, self).config(**kwargs)
+        ifacesStr = ' '.join(["-i " + ifaceName for ifaceName in self.interfaces.keys()])
+        self.cmd('route add default gw %s' % self.gateway)
+        self.cmd('%s -4 -a -pf %s %s %s' % (self.binFile, self.pidFile, ifacesStr, self.serverIp))
+
+    def terminate(self, **kwargs):
+        self.cmd('kill -9 `cat %s`', self.pidFile)
+        self.cmd('rm -rf %s' % self.pidFile)
+        super(DhcpRelay, self).terminate()
+
 class TaggedDhcpClient(Host):
     def __init__(self, name, vlan, *args, **kwargs):
         super(TaggedDhcpClient, self).__init__(name, **kwargs)