pingping-lin | a7a13a8 | 2015-08-31 16:20:53 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | """ |
| 4 | Set up the SDN-IP function test topology |
| 5 | """ |
| 6 | |
| 7 | """ |
| 8 | AS1 = 64513, (SDN AS) |
| 9 | AS2 = 64514, reachable by 192.168.10.1, 192.168.20.1 |
| 10 | AS3 = 64516, reachable by 192.168.30.1 |
| 11 | AS4 = 64517, reachable by 192.168.40.1 |
| 12 | AS6 = 64520, reachable by 192.168.60.2, (route server 192.168.60.1) |
| 13 | """ |
| 14 | |
| 15 | from mininet.net import Mininet |
| 16 | from mininet.node import Controller, RemoteController |
| 17 | from mininet.log import setLogLevel, info |
| 18 | from mininet.cli import CLI |
| 19 | from mininet.topo import Topo |
| 20 | from mininet.util import quietRun |
| 21 | from mininet.moduledeps import pathCheck |
| 22 | |
| 23 | import os.path |
| 24 | import time |
| 25 | from subprocess import Popen, STDOUT, PIPE |
| 26 | |
| 27 | QUAGGA_DIR = '/usr/lib/quagga' |
| 28 | QUAGGA_RUN_DIR = '/usr/local/var/run/quagga' |
| 29 | |
| 30 | |
| 31 | class SDNTopo( Topo ): |
| 32 | "SDN Topology" |
| 33 | |
| 34 | def __init__( self, *args, **kwargs ): |
| 35 | global numHost101 |
| 36 | global numHost200 |
| 37 | numHost101 = 101 |
| 38 | numHost200 = 200 |
| 39 | Topo.__init__( self, *args, **kwargs ) |
| 40 | sw1 = self.addSwitch( 'sw1', dpid = '00000000000000a1' ) |
| 41 | sw2 = self.addSwitch( 'sw2', dpid = '00000000000000a2' ) |
| 42 | sw3 = self.addSwitch( 'sw3', dpid = '00000000000000a3' ) |
| 43 | sw4 = self.addSwitch( 'sw4', dpid = '00000000000000a4' ) |
| 44 | sw5 = self.addSwitch( 'sw5', dpid = '00000000000000a5' ) |
| 45 | sw6 = self.addSwitch( 'sw6', dpid = '00000000000000a6' ) |
| 46 | # add a switch for 100 quagga hosts |
| 47 | sw100 = self.addSwitch( 'sw100', dpid = '0000000000000100' ) |
| 48 | swTestOn = self.addSwitch( 'swTestOn', dpid = '0000000000000102' ) |
| 49 | # Note this switch isn't part of the SDN topology |
| 50 | # We'll use the ovs-controller to turn this into a learning switch |
| 51 | as6sw = self.addSwitch( 'as6sw', dpid = '00000000000000a7' ) |
| 52 | |
| 53 | host1 = self.addHost( 'host1' ) |
| 54 | host2 = self.addHost( 'host2' ) |
| 55 | root1 = self.addHost( 'root1', inNamespace = False , ip = '0' ) |
| 56 | root2 = self.addHost( 'root2', inNamespace = False, ip = '0' ) |
| 57 | rootTestOn = self.addHost( 'rootTestOn', inNamespace = False, ip = '0' ) |
| 58 | |
| 59 | # AS2 host |
| 60 | host3 = self.addHost( 'host3' ) |
| 61 | as2host = self.addHost( 'as2host' ) |
| 62 | # AS3 host |
| 63 | host4 = self.addHost( 'host4' ) |
| 64 | as3host = self.addHost( 'as3host' ) |
| 65 | # AS4 host |
| 66 | for i in range( numHost101, numHost200 + 1 ): |
| 67 | self.addHost( 'host%s' % ( i ) ) |
| 68 | |
| 69 | as4host = self.addHost( 'as4host' ) |
| 70 | for i in range( numHost101, numHost200 + 1 ): |
| 71 | self.addHost( 'as4host%s' % ( i ) ) |
| 72 | # AS6 host |
| 73 | as6rs = self.addHost( 'as6rs' ) |
| 74 | host5 = self.addHost( 'host5' ) |
| 75 | as6host = self.addHost( 'as6host' ) |
| 76 | |
| 77 | self.addLink( host1, sw1 ) |
| 78 | self.addLink( host2, sw1 ) |
| 79 | # Links to the multihomed AS |
| 80 | self.addLink( host3, sw3 ) |
| 81 | self.addLink( host3, sw5 ) |
| 82 | self.addLink( as2host, host3 ) |
| 83 | # Single links to the remaining two ASes |
| 84 | self.addLink( host4, sw2 ) |
| 85 | self.addLink( sw100, sw6 ) |
| 86 | self.addLink( as3host, host4 ) |
| 87 | for i in range( numHost101, numHost200 + 1 ): |
| 88 | self.addLink( 'host%s' % ( i ), sw100 ) |
| 89 | for i in range( numHost101, numHost200 + 1 ): |
| 90 | self.addLink( 'host%s' % ( i ), 'as4host%s' % ( i ) ) |
| 91 | |
| 92 | # AS3-AS4 link |
| 93 | # self.addLink( host4, host5) |
| 94 | # Add new AS6 to its bridge |
| 95 | self.addLink( as6rs, as6sw ) |
| 96 | self.addLink( host5, as6sw ) |
| 97 | self.addLink( as6host, host5 ) |
| 98 | # test the host behind the router(behind the router server) |
| 99 | ''' |
| 100 | for i in range(1, 10): |
| 101 | host = self.addHost('as6host%d' % i) |
| 102 | self.addLink(host, as6router) |
| 103 | ''' |
| 104 | # Internal Connection To Hosts |
| 105 | self.addLink( root1, host1 ) |
| 106 | self.addLink( root2, host2 ) |
| 107 | |
| 108 | self.addLink( sw1, sw2 ) |
| 109 | self.addLink( sw1, sw3 ) |
| 110 | self.addLink( sw2, sw4 ) |
| 111 | self.addLink( sw3, sw4 ) |
| 112 | self.addLink( sw3, sw5 ) |
| 113 | self.addLink( sw4, sw6 ) |
| 114 | self.addLink( sw5, sw6 ) |
| 115 | self.addLink( as6sw, sw4 ) |
| 116 | |
| 117 | self.addLink( swTestOn, rootTestOn ) |
| 118 | self.addLink( swTestOn, host3 ) |
| 119 | self.addLink( swTestOn, host4 ) |
| 120 | self.addLink( swTestOn, host5 ) |
| 121 | self.addLink( swTestOn, as2host ) |
| 122 | |
| 123 | for i in range( numHost101, numHost200 + 1 ): |
| 124 | self.addLink( swTestOn, 'host' + str( i ) ) |
| 125 | |
| 126 | def startsshd( host ): |
| 127 | "Start sshd on host" |
| 128 | info( '*** Starting sshd\n' ) |
| 129 | name, intf, ip = host.name, host.defaultIntf(), host.IP() |
| 130 | banner = '/tmp/%s.banner' % name |
| 131 | host.cmd( 'echo "Welcome to %s at %s" > %s' % ( name, ip, banner ) ) |
| 132 | host.cmd( '/usr/sbin/sshd -o "Banner %s"' % banner, '-o "UseDNS no"' ) |
| 133 | info( '***', host.name, 'is running sshd on', intf, 'at', ip, '\n' ) |
| 134 | |
| 135 | def startsshds ( hosts ): |
| 136 | for h in hosts: |
| 137 | startsshd( h ) |
| 138 | |
| 139 | def stopsshd(): |
| 140 | "Stop *all* sshd processes with a custom banner" |
| 141 | info( '*** Shutting down stale sshd/Banner processes ', |
| 142 | quietRun( "pkill -9 -f Banner" ), '\n' ) |
| 143 | |
| 144 | def startquagga( host, num, config_file ): |
| 145 | info( '*** Starting Quagga on %s\n' % host ) |
| 146 | zebra_cmd = \ |
| 147 | '%s/zebra -d -f ./zebra.conf -z %s/zserv%s.api -i %s/zebra%s.pid'\ |
| 148 | % ( QUAGGA_DIR, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num ) |
| 149 | quagga_cmd = '%s/bgpd -d -f %s -z %s/zserv%s.api -i %s/bgpd%s.pid' \ |
| 150 | % ( QUAGGA_DIR, config_file, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num ) |
| 151 | |
| 152 | print zebra_cmd |
| 153 | print quagga_cmd |
| 154 | |
| 155 | host.cmd( zebra_cmd ) |
| 156 | host.cmd( quagga_cmd ) |
| 157 | |
| 158 | def startquaggahost5( host, num ): |
| 159 | info( '*** Starting Quagga on %s\n' % host ) |
| 160 | zebra_cmd = \ |
| 161 | '%s/zebra -d -f ./zebra.conf -z %s/zserv%s.api -i %s/zebra%s.pid' \ |
| 162 | % ( QUAGGA_DIR, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num ) |
| 163 | quagga_cmd = \ |
| 164 | '%s/bgpd -d -f ./as4quaggas/quagga%s.conf -z %s/zserv%s.api -i %s/bgpd%s.pid'\ |
| 165 | % ( QUAGGA_DIR, num, QUAGGA_RUN_DIR, num, QUAGGA_RUN_DIR, num ) |
| 166 | |
| 167 | host.cmd( zebra_cmd ) |
| 168 | host.cmd( quagga_cmd ) |
| 169 | |
| 170 | |
| 171 | def stopquagga(): |
| 172 | quietRun( 'sudo pkill -9 -f bgpd' ) |
| 173 | quietRun( 'sudo pkill -9 -f zebra' ) |
| 174 | |
| 175 | def sdn1net(): |
| 176 | topo = SDNTopo() |
| 177 | info( '*** Creating network\n' ) |
| 178 | net = Mininet( topo = topo, controller = RemoteController ) |
| 179 | |
| 180 | host1, host2, host3, host4, host5 = \ |
| 181 | net.get( 'host1', 'host2' , 'host3', 'host4', 'host5' ) |
| 182 | |
| 183 | # Adding 2nd, 3rd and 4th interface to host1 connected to sw1 |
| 184 | # for another BGP peering |
| 185 | host1.setMAC( '00:00:00:00:00:01', 'host1-eth0' ) |
| 186 | host1.cmd( 'ip addr add 192.168.20.101/24 dev host1-eth0' ) |
| 187 | host1.cmd( 'ip addr add 192.168.30.101/24 dev host1-eth0' ) |
| 188 | host1.cmd( 'ip addr add 192.168.40.101/24 dev host1-eth0' ) |
| 189 | host1.cmd( 'ip addr add 192.168.60.101/24 dev host1-eth0' ) |
| 190 | |
| 191 | # Net has to be start after adding the above link |
| 192 | net.start() |
| 193 | for i in range( numHost101, numHost200 + 1 ): |
| 194 | host100 = net.get( 'host%s' % ( i ) ) |
| 195 | host100.cmd( 'ifconfig host%s-eth0 192.168.40.%s up' % ( i, i - 100 ) ) |
| 196 | host100.setIP( str( i ) + ".0.0.254", 8, str( host100 ) + "-eth1" ) |
| 197 | host100.setMAC( '00:00:' + str( i - 101 ) + ':00:00:90', 'host' |
| 198 | + str( i ) + '-eth0' ) |
| 199 | host100.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' ) |
| 200 | |
| 201 | # Set up as6sw as a learning switch as quickly as possible so it |
| 202 | # hopefully doesn't connect to the actual controller |
| 203 | # TODO figure out how to change controller before starting switch |
| 204 | as6sw = net.get( 'as6sw' ) |
| 205 | as6sw.cmd( 'ovs-vsctl set-controller as6sw none' ) |
| 206 | as6sw.cmd( 'ovs-vsctl set-fail-mode as6sw standalone' ) |
| 207 | |
| 208 | |
| 209 | sw1, sw2, sw3, sw4, sw5, sw6 = \ |
| 210 | net.get( 'sw1', 'sw2', 'sw3', 'sw4', 'sw5', 'sw6' ) |
Charles Chan | 029be65 | 2015-08-24 01:46:10 +0800 | [diff] [blame] | 211 | sw1.cmd( 'ovs-vsctl set-controller sw1 tcp:10.128.4.52:6653' ) |
| 212 | sw2.cmd( 'ovs-vsctl set-controller sw2 tcp:10.128.4.52:6653' ) |
| 213 | sw3.cmd( 'ovs-vsctl set-controller sw3 tcp:10.128.4.52:6653' ) |
| 214 | sw4.cmd( 'ovs-vsctl set-controller sw4 tcp:10.128.4.52:6653' ) |
| 215 | sw5.cmd( 'ovs-vsctl set-controller sw5 tcp:10.128.4.52:6653' ) |
| 216 | sw6.cmd( 'ovs-vsctl set-controller sw6 tcp:10.128.4.52:6653' ) |
pingping-lin | a7a13a8 | 2015-08-31 16:20:53 -0700 | [diff] [blame] | 217 | |
| 218 | |
| 219 | # Set up sw100 as a learning |
| 220 | sw100 = net.get( 'sw100' ) |
| 221 | sw100.cmd( 'ovs-vsctl set-controller sw100 none' ) |
| 222 | sw100.cmd( 'ovs-vsctl set-fail-mode sw100 standalone' ) |
| 223 | |
| 224 | swTestOn = net.get( 'swTestOn' ) |
| 225 | swTestOn.cmd( 'ovs-vsctl set-controller swTestOn none' ) |
| 226 | swTestOn.cmd( 'ovs-vsctl set-fail-mode swTestOn standalone' ) |
| 227 | |
| 228 | host1.defaultIntf().setIP( '192.168.10.101/24' ) |
| 229 | |
| 230 | # Configure new host interfaces |
| 231 | host2.defaultIntf().setIP( '172.16.10.2/24' ) |
| 232 | host2.defaultIntf().setMAC( '00:00:00:00:01:02' ) |
| 233 | |
| 234 | # Set up AS2 |
| 235 | host3.setIP( '192.168.10.1', 24, 'host3-eth0' ) |
| 236 | host3.setIP( '192.168.20.1', 24, 'host3-eth1' ) |
| 237 | host3.setMAC( '00:00:00:00:02:01', 'host3-eth0' ) |
| 238 | host3.setMAC( '00:00:00:00:02:02', 'host3-eth1' ) |
| 239 | host3.setIP( '3.0.0.254', 8, 'host3-eth2' ) |
| 240 | host3.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' ) |
| 241 | |
| 242 | host3.setIP( '1.168.30.2', 24, 'host3-eth3' ) |
| 243 | host3.cmd( 'sysctl net.ipv4.conf.all.arp_ignore=1' ) |
| 244 | host3.cmd( 'sysctl net.ipv4.conf.all.arp_announce=1' ) |
| 245 | as2host = net.get( 'as2host' ) |
| 246 | |
| 247 | for i in range( 0, 20 ): |
| 248 | as2host.cmd( 'sudo ip addr add 3.0.%d.1/24 dev as2host-eth0' % i ) |
| 249 | as2host.setIP( '1.168.30.100', 24, 'as2host-eth1' ) |
| 250 | |
| 251 | as2host.cmd( 'ip route add default via 3.0.0.254' ) |
| 252 | |
| 253 | # Set up AS3 |
| 254 | host4.setIP( '192.168.30.1', 24, 'host4-eth0' ) |
| 255 | host4.setMAC( '00:00:00:00:03:01', 'host4-eth0' ) |
| 256 | host4.setIP( '4.0.0.254', 8, 'host4-eth1' ) |
| 257 | host4.setMAC( '00:00:00:00:03:99', 'host4-eth1' ) |
| 258 | host4.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' ) |
| 259 | as3host = net.get( 'as3host' ) |
| 260 | for i in range( 0, 20 ): |
| 261 | as3host.cmd( 'sudo ip addr add 4.0.%d.1/24 dev as3host-eth0' % i ) |
| 262 | as3host.cmd( 'ip route add default via 4.0.0.254' ) |
| 263 | |
| 264 | # root space |
| 265 | host4.setIP( '1.168.30.3', 24, 'host4-eth2' ) |
| 266 | host4.setMAC( '00:00:00:00:03:03', 'host4-eth2' ) |
| 267 | |
| 268 | # setup interface address for 100 quagga hosts |
| 269 | time.sleep( 10 ) |
| 270 | for i in range( numHost101, numHost200 + 1 ): |
| 271 | as4host100 = net.get( 'as4host%s' % ( i ) ) |
| 272 | as4host100.defaultIntf().setIP( str( i ) + '.0.0.1/24' ) |
| 273 | as4host100.cmd( 'ip route add default via ' + str( i ) + '.0.0.254' ) |
| 274 | for j in range( 0, 100 ): |
| 275 | as4host100.cmd( 'sudo ip addr add %d.0.%d.1/24 dev %s-eth0' \ |
| 276 | % ( i, j, as4host100 ) ) |
| 277 | |
| 278 | # Set up AS6 - This has a router and a route server |
| 279 | as6rs, host5 = net.get( 'as6rs', 'host5' ) |
| 280 | as6rs.setIP( '192.168.60.1', 24, 'as6rs-eth0' ) |
| 281 | as6rs.setMAC( '00:00:00:00:06:01', 'as6rs-eth0' ) |
| 282 | host5.setIP( '192.168.60.2', 24, 'host5-eth0' ) |
| 283 | host5.setMAC( '00:00:00:00:06:02', 'host5-eth0' ) |
| 284 | host5.setIP( '5.0.0.254', 8, 'host5-eth1' ) |
| 285 | host5.cmd( 'sysctl net.ipv4.conf.all.forwarding=1' ) |
| 286 | host5.setIP( '1.168.30.5', 24, 'host5-eth2' ) |
| 287 | host5.setMAC( '00:00:00:00:06:05', 'host5-eth2' ) |
| 288 | |
| 289 | as6host = net.get( 'as6host' ) |
| 290 | as6host.defaultIntf().setIP( '5.0.0.1/24' ) |
| 291 | as6host.cmd( 'ip route add default via 5.0.0.254' ) |
| 292 | for i in range( 0, 10 ): |
| 293 | as6host.cmd( 'sudo ip addr add 5.0.%d.1/24 dev as6host-eth0' % i ) |
| 294 | |
| 295 | # test the host in the as6 |
| 296 | ''' |
| 297 | for i in range(1, 10): |
| 298 | baseip = (i-1)*4 |
| 299 | host = net.get('as6host%d' % i) |
| 300 | host.defaultIntf().setIP('172.16.70.%d/24' % (baseip+1)) |
| 301 | host.cmd('ip route add default via 172.16.70.%d' % (baseip+2)) |
| 302 | as6router.setIP('172.16.70.%d' % (baseip+2), 30, 'as6router-eth%d' % (i+1)) |
| 303 | ''' |
| 304 | |
| 305 | # Start Quagga on border routers |
| 306 | startquagga( host3, 1, 'quagga1.conf' ) |
| 307 | startquagga( host4, 2, 'quagga2.conf' ) |
| 308 | for i in range( numHost101, numHost200 + 1 ): |
| 309 | host100 = net.get( 'host%d' % ( i ) ) |
| 310 | startquaggahost5( host100, i ) |
| 311 | |
| 312 | startquagga( as6rs, 4, 'quagga-as6-rs.conf' ) |
| 313 | startquagga( host5, 5, 'quagga-as6.conf' ) |
| 314 | |
| 315 | root1, root2, rootTestOn = net.get( 'root1', 'root2', 'rootTestOn' ) |
| 316 | host1.intf( 'host1-eth1' ).setIP( '1.1.1.1/24' ) |
| 317 | root1.intf( 'root1-eth0' ).setIP( '1.1.1.2/24' ) |
| 318 | host2.intf( 'host2-eth1' ).setIP( '1.1.2.1/24' ) |
| 319 | root2.intf( 'root2-eth0' ).setIP( '1.1.2.2/24' ) |
| 320 | |
| 321 | rootTestOn.cmd( 'ip addr add 1.168.30.99/24 dev rootTestOn-eth0' ) |
| 322 | |
| 323 | stopsshd() |
| 324 | for i in range( numHost101, numHost200 + 1 ): |
| 325 | hostX = net.get( 'host%s' % ( i ) ) |
| 326 | hostX.setIP( '1.168.30.' + str( i ), 24, str( "host" ) + str( i ) |
| 327 | + "-eth2" ) |
| 328 | startsshd( hostX ) |
| 329 | |
| 330 | startquagga( host1, 100, 'quagga-sdn.conf' ) |
| 331 | hosts = [ host1, host2, host3, host4, host5, as2host ]; |
| 332 | startsshds( hosts ) |
| 333 | # |
| 334 | onos1 = '10.128.4.52' |
| 335 | forwarding1 = '%s:2000:%s:2000' % ( '1.1.1.2', onos1 ) |
| 336 | root1.cmd( 'ssh -nNT -o "PasswordAuthentication no" \ |
| 337 | -o "StrictHostKeyChecking no" -l sdn -L %s %s & ' % ( forwarding1, onos1 ) ) |
| 338 | |
| 339 | # Forward 2605 to root namespace for easier access to SDN domain BGPd |
| 340 | # If root can ssh to itself without a password this should work |
| 341 | ''' |
| 342 | root1.cmd('ssh -N -o "PasswordAuthentication no" \ |
| 343 | -o "StrictHostKeyChecking no" -L 2605:1.1.1.1:2605 1.1.1.1 &') |
| 344 | ''' |
pingping-lin | 500727a | 2015-09-02 19:53:11 -0700 | [diff] [blame] | 345 | # time.sleep( 3000000000 ) |
pingping-lin | a7a13a8 | 2015-08-31 16:20:53 -0700 | [diff] [blame] | 346 | CLI( net ) |
| 347 | |
| 348 | # Close the ssh port forwarding |
| 349 | # quietRun('sudo pkill -f 1.1.1.1') |
| 350 | |
| 351 | stopsshd() |
| 352 | stopquagga() |
| 353 | net.stop() |
| 354 | |
| 355 | if __name__ == '__main__': |
| 356 | setLogLevel( 'debug' ) |
| 357 | sdn1net() |