blob: b87ec9f0f3fde51150fbb7c9b9a53dadd3782bb5 [file] [log] [blame]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -07001#!/usr/bin/python
2
3"""
4Libraries for Trellis hosts.
5"""
6
You Wang6cd51b42018-03-08 15:28:25 -08007import time
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -07008import sys
9sys.path.append('..')
10from mininet.node import Host
11from routinglib import RoutedHost, RoutedHost6, Router
12
13class TaggedRoutedHost(RoutedHost):
14 """Host that can be configured with multiple IP addresses."""
15 def __init__(self, name, ips, gateway, vlan, *args, **kwargs):
16 super(RoutedHost, self).__init__(name, *args, **kwargs)
17 self.ips = ips
18 self.gateway = gateway
19 self.vlan = vlan
20 self.vlanIntf = None
21
22 def config(self, **kwargs):
23 Host.config(self, **kwargs)
24 intf = self.defaultIntf()
25 self.vlanIntf = "%s.%s" % (intf, self.vlan)
26 self.cmd('ip -4 addr flush dev %s' % intf)
27 self.cmd('ip link add link %s name %s type vlan id %s' % (intf, self.vlanIntf, self.vlan))
28 self.cmd('ip link set up %s' % self.vlanIntf)
29
30 for ip in self.ips:
31 self.cmd('ip addr add %s dev %s' % (ip, self.vlanIntf))
32
33 self.cmd('ip route add default via %s' % self.gateway)
34 intf.name = self.vlanIntf
35 self.nameToIntf[self.vlanIntf] = intf
36
37 def terminate(self, **kwargs):
38 self.cmd('ip link remove link %s' % self.vlanIntf)
39 super(TaggedRoutedHost, self).terminate()
40
41class DualHomedRoutedHost(Host):
42 def __init__(self, name, ips, gateway, *args, **kwargs):
43 super(DualHomedRoutedHost, self).__init__(name, **kwargs)
44 self.bond0 = None
45 self.ips = ips
46 self.gateway = gateway
47
48 def config(self, **kwargs):
49 super(DualHomedRoutedHost, self).config(**kwargs)
Jon Halle9fcd342018-02-21 14:59:35 -080050 self.bondIntfs( self.intfs[0], self.intfs[1] )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070051
52 for ip in self.ips:
53 self.cmd('ip addr add %s dev %s' % (ip, self.bond0))
54
55 self.cmd('ip route add default via %s' % self.gateway)
Jon Halle9fcd342018-02-21 14:59:35 -080056
57 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
58 '''
59 Bond two interfaces together
60 intf1 - the first interface to bond
61 intf2 - the second interface to bond
62 bondedName - the prefix of the new interface name
63 '''
64 # Setup bonded interface
65 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
66 self.bond0 = "%s-%s" % ( self.name, bondedName )
67 self.cmd('modprobe bonding')
68 self.cmd('ip link add %s type bond' % self.bond0)
69 self.cmd('ip link set %s down' % intf1.name)
70 self.cmd('ip link set %s down' % intf2.name)
71 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
72 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
73 self.cmd('ip addr flush dev %s' % intf1.name)
74 self.cmd('ip addr flush dev %s' % intf2.name)
75 self.cmd('ip link set %s up' % self.bond0)
76 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
77 # or create a custom class to handle bonded infs??
78 lowestIntf = min( [ intf1, intf2 ] )
79 highestIntf = max( [ intf1, intf2 ] )
80 lowestIntf.name = self.bond0
81 self.nameToIntf[self.bond0] = lowestIntf
82 del self.intfs[ self.ports[ highestIntf ] ]
83 del self.ports[ highestIntf ]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070084
85 def terminate(self, **kwargs):
86 self.cmd('ip link set %s down' % self.bond0)
87 self.cmd('ip link delete %s' % self.bond0)
88 super(DualHomedRoutedHost, self).terminate()
89
90class DualHomedTaggedRoutedHost(DualHomedRoutedHost):
91 def __init__(self, name, ips, gateway, vlan, *args, **kwargs):
92 super(DualHomedTaggedRoutedHost, self).__init__(name, ips, gateway, *args, **kwargs)
93 self.ips = ips
94 self.gateway = gateway
95 self.vlan = vlan
96 self.vlanIntf = None
97
98 def config(self, **kwargs):
99 super(DualHomedTaggedRoutedHost, self).config(**kwargs)
100 default_intf = self.defaultIntf()
101 self.vlanIntf = "%s.%s" % (default_intf, self.vlan)
102 self.cmd('ip -4 addr flush dev %s' % default_intf)
103 self.cmd('ip link add link %s name %s type vlan id %s' % (default_intf, self.vlanIntf, self.vlan))
104 self.cmd('ip link set up %s' % self.vlanIntf)
105
106 for ip in self.ips:
107 self.cmd('ip addr add %s dev %s' % (ip, self.vlanIntf))
108
109 self.cmd('ip route add default via %s' % self.gateway)
110 default_intf.name = self.vlanIntf
111 self.nameToIntf[self.vlanIntf] = default_intf
112
113 def terminate(self, **kwargs):
114 self.cmd('ip link remove link %s' % self.vlanIntf)
115 super(DualHomedTaggedRoutedHost, self).terminate()
116
117class DhcpClient(Host):
118 def __init__(self, name, *args, **kwargs):
119 super(DhcpClient, self).__init__(name, **kwargs)
120 self.pidFile = '/run/dhclient-%s.pid' % self.name
121 self.leaseFile = '/var/lib/dhcp/dhcpclient-%s.lease' % (self.name, )
122
123 def config(self, **kwargs):
124 super(DhcpClient, self).config(**kwargs)
125 self.cmd('ip addr flush dev %s' % self.defaultIntf())
126 self.cmd('dhclient -q -4 -nw -pf %s -lf %s %s' % (self.pidFile, self.leaseFile, self.defaultIntf()))
127
128 def terminate(self, **kwargs):
129 self.cmd('kill -9 `cat %s`' % self.pidFile)
130 self.cmd('rm -rf %s' % self.pidFile)
131 super(DhcpClient, self).terminate()
132
133class Dhcp6Client(Host):
134 def __init__(self, name, *args, **kwargs):
135 super(Dhcp6Client, self).__init__(name, **kwargs)
136 self.pidFile = '/run/dhclient-%s.pid' % self.name
137 self.leaseFile = '/var/lib/dhcp/dhcpclient6-%s.lease' % (self.name, )
138
139 def config(self, **kwargs):
140 super(Dhcp6Client, self).config(**kwargs)
141 self.cmd('ip -4 addr flush dev %s' % self.defaultIntf())
142 self.cmd('dhclient -q -6 -nw -pf %s -lf %s %s' % (self.pidFile, self.leaseFile, self.defaultIntf()))
143
144 def terminate(self, **kwargs):
145 self.cmd('kill -9 `cat %s`' % self.pidFile)
146 self.cmd('rm -rf %s' % self.pidFile)
147 super(Dhcp6Client, self).terminate()
148
149class DhcpServer(RoutedHost):
150 binFile = '/usr/sbin/dhcpd'
151 pidFile = '/run/dhcp-server-dhcpd.pid'
152 configFile = './dhcpd.conf'
153 leasesFile = '/var/lib/dhcp/dhcpd.leases'
154
155 def config(self, **kwargs):
156 super(DhcpServer, self).config(**kwargs)
157 self.cmd('touch %s' % self.leasesFile)
158 self.cmd('%s -q -4 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.defaultIntf()))
159
160 def terminate(self, **kwargs):
161 self.cmd('kill -9 `cat %s`' % self.pidFile)
162 self.cmd('rm -rf %s' % self.pidFile)
163 super(DhcpServer, self).terminate()
164
165class Dhcp6Server(RoutedHost6):
166 binFile = '/usr/sbin/dhcpd'
167 pidFile = '/run/dhcp-server-dhcpd6.pid'
168 configFile = './dhcpd6.conf'
169 leasesFile = '/var/lib/dhcp/dhcpd6.leases'
170
171 def config(self, **kwargs):
172 super(Dhcp6Server, self).config(**kwargs)
173 linkLocalAddr = mac_to_ipv6_linklocal(kwargs['mac'])
174 self.cmd('ip -6 addr add dev %s scope link %s' % (self.defaultIntf(), linkLocalAddr))
175 self.cmd('touch %s' % self.leasesFile)
176 self.cmd('%s -q -6 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.defaultIntf()))
177
178 def terminate(self, **kwargs):
179 self.cmd('kill -9 `cat %s`' % self.pidFile)
180 self.cmd('rm -rf %s' % self.pidFile)
181 self.cmd('rm -rf %s' % self.leasesFile)
182 super(Dhcp6Server, self).terminate()
183
184class DhcpRelay(Router):
185 binFile = '/usr/sbin/dhcrelay'
186 pidFile = '/run/dhcp-relay.pid'
187 serverIp = None
188 gateway = None
189
190 def __init__(self, name, serverIp, gateway, *args, **kwargs):
191 super(DhcpRelay, self).__init__(name, **kwargs)
192 self.serverIp = serverIp
193 self.gateway = gateway
194
195 def config(self, **kwargs):
196 super(DhcpRelay, self).config(**kwargs)
197 ifacesStr = ' '.join(["-i " + ifaceName for ifaceName in self.interfaces.keys()])
198 self.cmd('route add default gw %s' % self.gateway)
199 self.cmd('%s -4 -a -pf %s %s %s' % (self.binFile, self.pidFile, ifacesStr, self.serverIp))
200
201 def terminate(self, **kwargs):
202 self.cmd('kill -9 `cat %s`', self.pidFile)
203 self.cmd('rm -rf %s' % self.pidFile)
204 super(DhcpRelay, self).terminate()
205
206class TaggedDhcpClient(Host):
207 def __init__(self, name, vlan, *args, **kwargs):
208 super(TaggedDhcpClient, self).__init__(name, **kwargs)
209 self.pidFile = '/run/dhclient-%s.pid' % self.name
210 self.vlan = vlan
211 self.vlanIntf = None
212
213 def config(self, **kwargs):
214 super(TaggedDhcpClient, self).config(**kwargs)
215 self.vlanIntf = "%s.%s" % (self.defaultIntf(), self.vlan)
216 self.cmd('ip addr flush dev %s' % self.defaultIntf())
217 self.cmd('ip link add link %s name %s type vlan id %s' % (self.defaultIntf(), self.vlanIntf, self.vlan))
218 self.cmd('ip link set up %s' % self.vlanIntf)
219 self.cmd('dhclient -q -4 -nw -pf %s %s' % (self.pidFile, self.vlanIntf))
220
221 def terminate(self, **kwargs):
222 self.cmd('kill -9 `cat %s`' % self.pidFile)
223 self.cmd('rm -rf %s' % self.pidFile)
224 self.cmd('ip link remove link %s' % self.vlanIntf)
225 super(TaggedDhcpClient, self).terminate()
226
227class TaggedDhcpServer(TaggedRoutedHost):
228 binFile = '/usr/sbin/dhcpd'
229 pidFile = '/run/dhcp-server/dhcpd.pid'
230 configFile = './dhcpd.conf'
231
232 def config(self, **kwargs):
233 super(TaggedDhcpServer, self).config(**kwargs)
234 self.cmd('%s -q -4 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.vlanIntf))
235
236 def terminate(self, **kwargs):
237 self.cmd('kill -9 `cat %s`' % self.pidFile)
238 self.cmd('rm -rf %s' % self.pidFile)
239 super(TaggedDhcpServer, self).terminate()
240
241class DualHomedDhcpClient(Host):
242 def __init__(self, name, *args, **kwargs):
243 super(DualHomedDhcpClient, self).__init__(name, **kwargs)
244 self.pidFile = '/run/dhclient-%s.pid' % self.name
245 self.bond0 = None
246
247 def config(self, **kwargs):
248 super(DualHomedDhcpClient, self).config(**kwargs)
Jon Halle9fcd342018-02-21 14:59:35 -0800249 self.bondIntfs( self.intfs[0], self.intfs[1] )
250 self.cmd('dhclient -q -4 -nw -pf %s %s' % (self.pidFile, self.bond0))
251
252 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
253 '''
254 Bond two interfaces together
255 intf1 - the first interface to bond
256 intf2 - the second interface to bond
257 bondedName - the prefix of the new interface name
258 '''
259 # Setup bonded interface
260 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
261 self.bond0 = "%s-%s" % ( self.name, bondedName )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700262 self.cmd('modprobe bonding')
263 self.cmd('ip link add %s type bond' % self.bond0)
Jon Halle9fcd342018-02-21 14:59:35 -0800264 self.cmd('ip link set %s down' % intf1.name)
265 self.cmd('ip link set %s down' % intf2.name)
266 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
267 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
268 self.cmd('ip addr flush dev %s' % intf1.name)
269 self.cmd('ip addr flush dev %s' % intf2.name)
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700270 self.cmd('ip link set %s up' % self.bond0)
Jon Halle9fcd342018-02-21 14:59:35 -0800271 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
272 # or create a custom class to handle bonded infs??
273 lowestIntf = min( [ intf1, intf2 ] )
274 highestIntf = max( [ intf1, intf2 ] )
275 lowestIntf.name = self.bond0
276 self.nameToIntf[self.bond0] = lowestIntf
277 del self.intfs[ self.ports[ highestIntf ] ]
278 del self.ports[ highestIntf ]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700279
280 def terminate(self, **kwargs):
281 self.cmd('ip link set %s down' % self.bond0)
282 self.cmd('ip link delete %s' % self.bond0)
283 self.cmd('kill -9 `cat %s`' % self.pidFile)
284 self.cmd('rm -rf %s' % self.pidFile)
285 super(DualHomedDhcpClient, self).terminate()
286
You Wang5102af12018-02-08 12:30:12 -0800287class TrellisHost(Host):
288 def __init__(self, name, ips=[], gateway="", dualHomed=False, vlan=None, dhcpClient=False, dhcpServer=False, ipv6=False, *args, **kwargs):
289 super(TrellisHost, self).__init__(name, *args, **kwargs)
290 self.dualHomed = dualHomed
291 self.bond0 = None
292 self.vlan = vlan
293 self.vlanIntf = None
294 self.dhcpClient = dhcpClient
295 self.dhcpServer = dhcpServer
296 if dhcpClient:
297 self.pidFile = '/run/dhclient-%s.pid' % self.name
298 self.leaseFile = '/var/lib/dhcp/dhcpclient%s-%s.lease' % ("6" if ipv6 else "", self.name)
299 else:
300 self.ips = ips
301 self.gateway = gateway
302 if dhcpServer:
303 self.binFile = '/usr/sbin/dhcpd'
304 self.pidFile = '/run/dhcp-server-dhcpd%s.pid' % ("6" if ipv6 else "")
305 self.configFile = './dhcpd%s.conf' % ("6" if ipv6 else "")
306 self.leasesFile = '/var/lib/dhcp/dhcpd%s.leases' % ("6" if ipv6 else "")
307 self.ipv6 = ipv6
308
309 def config(self, **kwargs):
310 super(TrellisHost, self).config(**kwargs)
311
312 if self.dualHomed:
Jon Halle9fcd342018-02-21 14:59:35 -0800313 self.bondIntfs( self.intfs[0], self.intfs[1] )
You Wang5102af12018-02-08 12:30:12 -0800314
315 self.cmd('ip %s addr flush dev %s' % ("-4" if self.ipv6 else "", self.defaultIntf()))
316
317 if self.vlan:
318 # Setup vlan interface
319 defaultIntf = self.defaultIntf()
320 self.vlanIntf = "%s.%s" % (defaultIntf, self.vlan)
321 self.cmd('ip link add link %s name %s type vlan id %s' % (defaultIntf, self.vlanIntf, self.vlan))
322 self.cmd('ip link set up %s' % self.vlanIntf)
323 defaultIntf.name = self.vlanIntf
324 self.nameToIntf[self.vlanIntf] = defaultIntf
325
326 if self.dhcpClient:
You Wang6cd51b42018-03-08 15:28:25 -0800327 time.sleep(3)
You Wangb097c0f2018-02-16 16:19:41 -0800328 self.cmd('dhclient -q -%s -nw -pf %s -lf %s %s' % (6 if self.ipv6 else 4, self.pidFile, self.leaseFile, self.defaultIntf()))
You Wang5102af12018-02-08 12:30:12 -0800329 else:
330 # Setup IP addresses
331 for ip in self.ips:
332 self.cmd('ip addr add %s dev %s' % (ip, self.defaultIntf()))
333 self.cmd('ip route add default via %s' % self.gateway)
334
335 if self.dhcpServer:
336 if self.ipv6:
337 linkLocalAddr = mac_to_ipv6_linklocal(kwargs['mac'])
338 self.cmd('ip -6 addr add dev %s scope link %s' % (self.defaultIntf(), linkLocalAddr))
339 self.cmd('touch %s' % self.leasesFile)
340 self.cmd('%s -q -%s -pf %s -cf %s %s' % (self.binFile, 6 if self.ipv6 else 4, self.pidFile, self.configFile, self.defaultIntf()))
341
Jon Halle9fcd342018-02-21 14:59:35 -0800342 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
343 '''
344 Bond two interfaces together
345 intf1 - the first interface to bond
346 intf2 - the second interface to bond
347 bondedName - the prefix of the new interface name
348 '''
349 # Setup bonded interface
350 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
351 self.bond0 = "%s-%s" % ( self.name, bondedName )
352 self.cmd('modprobe bonding')
353 self.cmd('ip link add %s type bond' % self.bond0)
354 self.cmd('ip link set %s down' % intf1.name)
355 self.cmd('ip link set %s down' % intf2.name)
356 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
357 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
358 self.cmd('ip addr flush dev %s' % intf1.name)
359 self.cmd('ip addr flush dev %s' % intf2.name)
360 self.cmd('ip link set %s up' % self.bond0)
361 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
362 # or create a custom class to handle bonded infs??
363 lowestIntf = min( [ intf1, intf2 ] )
364 highestIntf = max( [ intf1, intf2 ] )
365 lowestIntf.name = self.bond0
366 self.nameToIntf[self.bond0] = lowestIntf
367 del self.intfs[ self.ports[ highestIntf ] ]
368 del self.ports[ highestIntf ]
369
You Wang5102af12018-02-08 12:30:12 -0800370 def terminate(self, **kwargs):
371 if self.vlan:
372 self.cmd('ip link remove link %s' % self.vlanIntf)
373 if self.dualHomed:
374 self.cmd('ip link set %s down' % self.bond0)
375 self.cmd('ip link delete %s' % self.bond0)
376 if self.dhcpClient:
377 self.cmd('kill -9 `cat %s`' % self.pidFile)
378 self.cmd('rm -rf %s' % self.pidFile)
379 if self.dhcpServer:
380 self.cmd('kill -9 `cat %s`' % self.pidFile)
381 self.cmd('rm -rf %s' % self.pidFile)
382 super(TrellisHost, self).terminate()
383
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700384# Utility for IPv6
385def mac_to_ipv6_linklocal(mac):
386 '''
387 Convert mac address to link-local IPv6 address
388 '''
389 # Remove the most common delimiters; dots, dashes, etc.
390 mac_value = int(mac.translate(None, ' .:-'), 16)
391
392 # Split out the bytes that slot into the IPv6 address
393 # XOR the most significant byte with 0x02, inverting the
394 # Universal / Local bit
395 high2 = mac_value >> 32 & 0xffff ^ 0x0200
396 high1 = mac_value >> 24 & 0xff
397 low1 = mac_value >> 16 & 0xff
398 low2 = mac_value & 0xffff
399
400 return 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2)