blob: e02df4c412a58628c6aeac4a2f8dd901d66e3e32 [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
Jon Hall43060f62020-06-23 13:13:33 -070012from stratum import NoOffloadHost
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070013
14class TaggedRoutedHost(RoutedHost):
15 """Host that can be configured with multiple IP addresses."""
16 def __init__(self, name, ips, gateway, vlan, *args, **kwargs):
17 super(RoutedHost, self).__init__(name, *args, **kwargs)
18 self.ips = ips
19 self.gateway = gateway
20 self.vlan = vlan
21 self.vlanIntf = None
22
23 def config(self, **kwargs):
24 Host.config(self, **kwargs)
25 intf = self.defaultIntf()
26 self.vlanIntf = "%s.%s" % (intf, self.vlan)
27 self.cmd('ip -4 addr flush dev %s' % intf)
Jon Hall43060f62020-06-23 13:13:33 -070028 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070029 self.cmd('ip link add link %s name %s type vlan id %s' % (intf, self.vlanIntf, self.vlan))
30 self.cmd('ip link set up %s' % self.vlanIntf)
31
32 for ip in self.ips:
33 self.cmd('ip addr add %s dev %s' % (ip, self.vlanIntf))
34
35 self.cmd('ip route add default via %s' % self.gateway)
36 intf.name = self.vlanIntf
37 self.nameToIntf[self.vlanIntf] = intf
38
39 def terminate(self, **kwargs):
40 self.cmd('ip link remove link %s' % self.vlanIntf)
41 super(TaggedRoutedHost, self).terminate()
42
Jon Hall43060f62020-06-23 13:13:33 -070043class DualHomedRoutedHost(NoOffloadHost):
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070044 def __init__(self, name, ips, gateway, *args, **kwargs):
45 super(DualHomedRoutedHost, self).__init__(name, **kwargs)
46 self.bond0 = None
47 self.ips = ips
48 self.gateway = gateway
49
50 def config(self, **kwargs):
51 super(DualHomedRoutedHost, self).config(**kwargs)
Jon Halle9fcd342018-02-21 14:59:35 -080052 self.bondIntfs( self.intfs[0], self.intfs[1] )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070053
54 for ip in self.ips:
55 self.cmd('ip addr add %s dev %s' % (ip, self.bond0))
56
57 self.cmd('ip route add default via %s' % self.gateway)
Jon Halle9fcd342018-02-21 14:59:35 -080058
59 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
60 '''
61 Bond two interfaces together
62 intf1 - the first interface to bond
63 intf2 - the second interface to bond
64 bondedName - the prefix of the new interface name
65 '''
66 # Setup bonded interface
67 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
68 self.bond0 = "%s-%s" % ( self.name, bondedName )
69 self.cmd('modprobe bonding')
70 self.cmd('ip link add %s type bond' % self.bond0)
71 self.cmd('ip link set %s down' % intf1.name)
72 self.cmd('ip link set %s down' % intf2.name)
73 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
74 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
75 self.cmd('ip addr flush dev %s' % intf1.name)
76 self.cmd('ip addr flush dev %s' % intf2.name)
Jon Hall43060f62020-06-23 13:13:33 -070077 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jon Halle9fcd342018-02-21 14:59:35 -080078 self.cmd('ip link set %s up' % self.bond0)
79 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
80 # or create a custom class to handle bonded infs??
81 lowestIntf = min( [ intf1, intf2 ] )
82 highestIntf = max( [ intf1, intf2 ] )
83 lowestIntf.name = self.bond0
84 self.nameToIntf[self.bond0] = lowestIntf
85 del self.intfs[ self.ports[ highestIntf ] ]
86 del self.ports[ highestIntf ]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070087
88 def terminate(self, **kwargs):
89 self.cmd('ip link set %s down' % self.bond0)
90 self.cmd('ip link delete %s' % self.bond0)
91 super(DualHomedRoutedHost, self).terminate()
92
93class DualHomedTaggedRoutedHost(DualHomedRoutedHost):
94 def __init__(self, name, ips, gateway, vlan, *args, **kwargs):
95 super(DualHomedTaggedRoutedHost, self).__init__(name, ips, gateway, *args, **kwargs)
96 self.ips = ips
97 self.gateway = gateway
98 self.vlan = vlan
99 self.vlanIntf = None
100
101 def config(self, **kwargs):
102 super(DualHomedTaggedRoutedHost, self).config(**kwargs)
103 default_intf = self.defaultIntf()
104 self.vlanIntf = "%s.%s" % (default_intf, self.vlan)
105 self.cmd('ip -4 addr flush dev %s' % default_intf)
Jon Hall43060f62020-06-23 13:13:33 -0700106 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700107 self.cmd('ip link add link %s name %s type vlan id %s' % (default_intf, self.vlanIntf, self.vlan))
108 self.cmd('ip link set up %s' % self.vlanIntf)
109
110 for ip in self.ips:
111 self.cmd('ip addr add %s dev %s' % (ip, self.vlanIntf))
112
113 self.cmd('ip route add default via %s' % self.gateway)
114 default_intf.name = self.vlanIntf
115 self.nameToIntf[self.vlanIntf] = default_intf
116
117 def terminate(self, **kwargs):
118 self.cmd('ip link remove link %s' % self.vlanIntf)
119 super(DualHomedTaggedRoutedHost, self).terminate()
120
121class DhcpClient(Host):
122 def __init__(self, name, *args, **kwargs):
123 super(DhcpClient, self).__init__(name, **kwargs)
124 self.pidFile = '/run/dhclient-%s.pid' % self.name
125 self.leaseFile = '/var/lib/dhcp/dhcpclient-%s.lease' % (self.name, )
126
127 def config(self, **kwargs):
128 super(DhcpClient, self).config(**kwargs)
129 self.cmd('ip addr flush dev %s' % self.defaultIntf())
Jon Hall43060f62020-06-23 13:13:33 -0700130 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700131 self.cmd('dhclient -q -4 -nw -pf %s -lf %s %s' % (self.pidFile, self.leaseFile, self.defaultIntf()))
132
133 def terminate(self, **kwargs):
134 self.cmd('kill -9 `cat %s`' % self.pidFile)
135 self.cmd('rm -rf %s' % self.pidFile)
136 super(DhcpClient, self).terminate()
137
138class Dhcp6Client(Host):
139 def __init__(self, name, *args, **kwargs):
140 super(Dhcp6Client, self).__init__(name, **kwargs)
141 self.pidFile = '/run/dhclient-%s.pid' % self.name
142 self.leaseFile = '/var/lib/dhcp/dhcpclient6-%s.lease' % (self.name, )
143
144 def config(self, **kwargs):
145 super(Dhcp6Client, self).config(**kwargs)
146 self.cmd('ip -4 addr flush dev %s' % self.defaultIntf())
Jon Hall43060f62020-06-23 13:13:33 -0700147 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700148 self.cmd('dhclient -q -6 -nw -pf %s -lf %s %s' % (self.pidFile, self.leaseFile, self.defaultIntf()))
149
150 def terminate(self, **kwargs):
151 self.cmd('kill -9 `cat %s`' % self.pidFile)
152 self.cmd('rm -rf %s' % self.pidFile)
153 super(Dhcp6Client, self).terminate()
154
155class DhcpServer(RoutedHost):
156 binFile = '/usr/sbin/dhcpd'
157 pidFile = '/run/dhcp-server-dhcpd.pid'
158 configFile = './dhcpd.conf'
159 leasesFile = '/var/lib/dhcp/dhcpd.leases'
160
161 def config(self, **kwargs):
162 super(DhcpServer, self).config(**kwargs)
163 self.cmd('touch %s' % self.leasesFile)
164 self.cmd('%s -q -4 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.defaultIntf()))
165
166 def terminate(self, **kwargs):
167 self.cmd('kill -9 `cat %s`' % self.pidFile)
168 self.cmd('rm -rf %s' % self.pidFile)
169 super(DhcpServer, self).terminate()
170
171class Dhcp6Server(RoutedHost6):
172 binFile = '/usr/sbin/dhcpd'
173 pidFile = '/run/dhcp-server-dhcpd6.pid'
174 configFile = './dhcpd6.conf'
175 leasesFile = '/var/lib/dhcp/dhcpd6.leases'
176
177 def config(self, **kwargs):
178 super(Dhcp6Server, self).config(**kwargs)
179 linkLocalAddr = mac_to_ipv6_linklocal(kwargs['mac'])
180 self.cmd('ip -6 addr add dev %s scope link %s' % (self.defaultIntf(), linkLocalAddr))
181 self.cmd('touch %s' % self.leasesFile)
182 self.cmd('%s -q -6 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.defaultIntf()))
183
184 def terminate(self, **kwargs):
185 self.cmd('kill -9 `cat %s`' % self.pidFile)
186 self.cmd('rm -rf %s' % self.pidFile)
187 self.cmd('rm -rf %s' % self.leasesFile)
188 super(Dhcp6Server, self).terminate()
189
190class DhcpRelay(Router):
191 binFile = '/usr/sbin/dhcrelay'
192 pidFile = '/run/dhcp-relay.pid'
193 serverIp = None
194 gateway = None
195
196 def __init__(self, name, serverIp, gateway, *args, **kwargs):
197 super(DhcpRelay, self).__init__(name, **kwargs)
198 self.serverIp = serverIp
199 self.gateway = gateway
200
201 def config(self, **kwargs):
202 super(DhcpRelay, self).config(**kwargs)
203 ifacesStr = ' '.join(["-i " + ifaceName for ifaceName in self.interfaces.keys()])
204 self.cmd('route add default gw %s' % self.gateway)
205 self.cmd('%s -4 -a -pf %s %s %s' % (self.binFile, self.pidFile, ifacesStr, self.serverIp))
206
207 def terminate(self, **kwargs):
208 self.cmd('kill -9 `cat %s`', self.pidFile)
209 self.cmd('rm -rf %s' % self.pidFile)
210 super(DhcpRelay, self).terminate()
211
212class TaggedDhcpClient(Host):
213 def __init__(self, name, vlan, *args, **kwargs):
214 super(TaggedDhcpClient, self).__init__(name, **kwargs)
215 self.pidFile = '/run/dhclient-%s.pid' % self.name
216 self.vlan = vlan
217 self.vlanIntf = None
218
219 def config(self, **kwargs):
220 super(TaggedDhcpClient, self).config(**kwargs)
221 self.vlanIntf = "%s.%s" % (self.defaultIntf(), self.vlan)
222 self.cmd('ip addr flush dev %s' % self.defaultIntf())
Jon Hall43060f62020-06-23 13:13:33 -0700223 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700224 self.cmd('ip link add link %s name %s type vlan id %s' % (self.defaultIntf(), self.vlanIntf, self.vlan))
225 self.cmd('ip link set up %s' % self.vlanIntf)
226 self.cmd('dhclient -q -4 -nw -pf %s %s' % (self.pidFile, self.vlanIntf))
227
228 def terminate(self, **kwargs):
229 self.cmd('kill -9 `cat %s`' % self.pidFile)
230 self.cmd('rm -rf %s' % self.pidFile)
231 self.cmd('ip link remove link %s' % self.vlanIntf)
232 super(TaggedDhcpClient, self).terminate()
233
234class TaggedDhcpServer(TaggedRoutedHost):
235 binFile = '/usr/sbin/dhcpd'
236 pidFile = '/run/dhcp-server/dhcpd.pid'
237 configFile = './dhcpd.conf'
238
239 def config(self, **kwargs):
240 super(TaggedDhcpServer, self).config(**kwargs)
241 self.cmd('%s -q -4 -pf %s -cf %s %s' % (self.binFile, self.pidFile, self.configFile, self.vlanIntf))
242
243 def terminate(self, **kwargs):
244 self.cmd('kill -9 `cat %s`' % self.pidFile)
245 self.cmd('rm -rf %s' % self.pidFile)
246 super(TaggedDhcpServer, self).terminate()
247
248class DualHomedDhcpClient(Host):
249 def __init__(self, name, *args, **kwargs):
250 super(DualHomedDhcpClient, self).__init__(name, **kwargs)
251 self.pidFile = '/run/dhclient-%s.pid' % self.name
252 self.bond0 = None
253
254 def config(self, **kwargs):
255 super(DualHomedDhcpClient, self).config(**kwargs)
Jon Halle9fcd342018-02-21 14:59:35 -0800256 self.bondIntfs( self.intfs[0], self.intfs[1] )
257 self.cmd('dhclient -q -4 -nw -pf %s %s' % (self.pidFile, self.bond0))
258
259 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
260 '''
261 Bond two interfaces together
262 intf1 - the first interface to bond
263 intf2 - the second interface to bond
264 bondedName - the prefix of the new interface name
265 '''
266 # Setup bonded interface
267 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
268 self.bond0 = "%s-%s" % ( self.name, bondedName )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700269 self.cmd('modprobe bonding')
270 self.cmd('ip link add %s type bond' % self.bond0)
Jon Halle9fcd342018-02-21 14:59:35 -0800271 self.cmd('ip link set %s down' % intf1.name)
272 self.cmd('ip link set %s down' % intf2.name)
273 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
274 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
275 self.cmd('ip addr flush dev %s' % intf1.name)
276 self.cmd('ip addr flush dev %s' % intf2.name)
Jon Hall43060f62020-06-23 13:13:33 -0700277 self.cmd('sysctl -w net.ipv4.ip_forward=0')
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700278 self.cmd('ip link set %s up' % self.bond0)
Jon Halle9fcd342018-02-21 14:59:35 -0800279 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
280 # or create a custom class to handle bonded infs??
281 lowestIntf = min( [ intf1, intf2 ] )
282 highestIntf = max( [ intf1, intf2 ] )
283 lowestIntf.name = self.bond0
284 self.nameToIntf[self.bond0] = lowestIntf
285 del self.intfs[ self.ports[ highestIntf ] ]
286 del self.ports[ highestIntf ]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700287
288 def terminate(self, **kwargs):
289 self.cmd('ip link set %s down' % self.bond0)
290 self.cmd('ip link delete %s' % self.bond0)
291 self.cmd('kill -9 `cat %s`' % self.pidFile)
292 self.cmd('rm -rf %s' % self.pidFile)
293 super(DualHomedDhcpClient, self).terminate()
294
Jon Hall43060f62020-06-23 13:13:33 -0700295class TrellisHost(NoOffloadHost):
You Wang5102af12018-02-08 12:30:12 -0800296 def __init__(self, name, ips=[], gateway="", dualHomed=False, vlan=None, dhcpClient=False, dhcpServer=False, ipv6=False, *args, **kwargs):
297 super(TrellisHost, self).__init__(name, *args, **kwargs)
298 self.dualHomed = dualHomed
299 self.bond0 = None
300 self.vlan = vlan
301 self.vlanIntf = None
302 self.dhcpClient = dhcpClient
303 self.dhcpServer = dhcpServer
304 if dhcpClient:
305 self.pidFile = '/run/dhclient-%s.pid' % self.name
306 self.leaseFile = '/var/lib/dhcp/dhcpclient%s-%s.lease' % ("6" if ipv6 else "", self.name)
307 else:
308 self.ips = ips
309 self.gateway = gateway
310 if dhcpServer:
311 self.binFile = '/usr/sbin/dhcpd'
312 self.pidFile = '/run/dhcp-server-dhcpd%s.pid' % ("6" if ipv6 else "")
313 self.configFile = './dhcpd%s.conf' % ("6" if ipv6 else "")
314 self.leasesFile = '/var/lib/dhcp/dhcpd%s.leases' % ("6" if ipv6 else "")
315 self.ipv6 = ipv6
316
317 def config(self, **kwargs):
318 super(TrellisHost, self).config(**kwargs)
319
320 if self.dualHomed:
Jon Halle9fcd342018-02-21 14:59:35 -0800321 self.bondIntfs( self.intfs[0], self.intfs[1] )
You Wang5102af12018-02-08 12:30:12 -0800322
323 self.cmd('ip %s addr flush dev %s' % ("-4" if self.ipv6 else "", self.defaultIntf()))
Jon Hall43060f62020-06-23 13:13:33 -0700324 self.cmd('sysctl -w net.ipv4.ip_forward=0')
325
326 if not self.ipv6:
327 self.cmd('sysctl -w net.ipv6.conf.all.disable_ipv6=1')
You Wang5102af12018-02-08 12:30:12 -0800328
329 if self.vlan:
330 # Setup vlan interface
331 defaultIntf = self.defaultIntf()
332 self.vlanIntf = "%s.%s" % (defaultIntf, self.vlan)
333 self.cmd('ip link add link %s name %s type vlan id %s' % (defaultIntf, self.vlanIntf, self.vlan))
334 self.cmd('ip link set up %s' % self.vlanIntf)
335 defaultIntf.name = self.vlanIntf
336 self.nameToIntf[self.vlanIntf] = defaultIntf
337
338 if self.dhcpClient:
You Wang6cd51b42018-03-08 15:28:25 -0800339 time.sleep(3)
You Wangb097c0f2018-02-16 16:19:41 -0800340 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 -0800341 else:
342 # Setup IP addresses
343 for ip in self.ips:
344 self.cmd('ip addr add %s dev %s' % (ip, self.defaultIntf()))
345 self.cmd('ip route add default via %s' % self.gateway)
346
347 if self.dhcpServer:
348 if self.ipv6:
349 linkLocalAddr = mac_to_ipv6_linklocal(kwargs['mac'])
350 self.cmd('ip -6 addr add dev %s scope link %s' % (self.defaultIntf(), linkLocalAddr))
351 self.cmd('touch %s' % self.leasesFile)
352 self.cmd('%s -q -%s -pf %s -cf %s %s' % (self.binFile, 6 if self.ipv6 else 4, self.pidFile, self.configFile, self.defaultIntf()))
353
Jon Halle9fcd342018-02-21 14:59:35 -0800354 def bondIntfs( self, intf1, intf2, bondedName="bond0" ):
355 '''
356 Bond two interfaces together
357 intf1 - the first interface to bond
358 intf2 - the second interface to bond
359 bondedName - the prefix of the new interface name
360 '''
361 # Setup bonded interface
362 # TODO: support multiple bonded interfaces. Maybe just changed self.bond0 to a list of bonded intf names?
363 self.bond0 = "%s-%s" % ( self.name, bondedName )
364 self.cmd('modprobe bonding')
365 self.cmd('ip link add %s type bond' % self.bond0)
366 self.cmd('ip link set %s down' % intf1.name)
367 self.cmd('ip link set %s down' % intf2.name)
368 self.cmd('ip link set %s master %s' % (intf1.name, self.bond0))
369 self.cmd('ip link set %s master %s' % (intf2.name, self.bond0))
370 self.cmd('ip addr flush dev %s' % intf1.name)
371 self.cmd('ip addr flush dev %s' % intf2.name)
372 self.cmd('ip link set %s up' % self.bond0)
373 # NOTE: Issues with bonded intfs in mn data structures. Either only show bonded intf
374 # or create a custom class to handle bonded infs??
375 lowestIntf = min( [ intf1, intf2 ] )
376 highestIntf = max( [ intf1, intf2 ] )
377 lowestIntf.name = self.bond0
378 self.nameToIntf[self.bond0] = lowestIntf
379 del self.intfs[ self.ports[ highestIntf ] ]
380 del self.ports[ highestIntf ]
381
You Wang5102af12018-02-08 12:30:12 -0800382 def terminate(self, **kwargs):
383 if self.vlan:
384 self.cmd('ip link remove link %s' % self.vlanIntf)
385 if self.dualHomed:
386 self.cmd('ip link set %s down' % self.bond0)
387 self.cmd('ip link delete %s' % self.bond0)
388 if self.dhcpClient:
389 self.cmd('kill -9 `cat %s`' % self.pidFile)
390 self.cmd('rm -rf %s' % self.pidFile)
You Wangafcbeff2018-03-26 15:48:07 -0700391 self.cmd('rm -rf %s' % self.leaseFile)
You Wang5102af12018-02-08 12:30:12 -0800392 if self.dhcpServer:
393 self.cmd('kill -9 `cat %s`' % self.pidFile)
394 self.cmd('rm -rf %s' % self.pidFile)
395 super(TrellisHost, self).terminate()
396
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700397# Utility for IPv6
398def mac_to_ipv6_linklocal(mac):
399 '''
400 Convert mac address to link-local IPv6 address
401 '''
402 # Remove the most common delimiters; dots, dashes, etc.
403 mac_value = int(mac.translate(None, ' .:-'), 16)
404
405 # Split out the bytes that slot into the IPv6 address
406 # XOR the most significant byte with 0x02, inverting the
407 # Universal / Local bit
408 high2 = mac_value >> 32 & 0xffff ^ 0x0200
409 high1 = mac_value >> 24 & 0xff
410 low1 = mac_value >> 16 & 0xff
411 low2 = mac_value & 0xffff
412
413 return 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2)