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