def saveVPN(self): self.properties['vid'] = self. vid self.properties['priority'] = self.priority self.properties['meter'] = self.meter self.properties['mat'] = str(self.mat.serialize()) if not 'pops' in self.properties: self.properties['pops'] = {} for p in self.pops: self.properties['pops'][p] = p #self.properties['pops'] = tmp self.properties['vpnsites'] = str(self.vpnsites) self.properties['vpnsitevlans'] = str(self.vpnsitevlans) self.properties['entryfanoutflows'] = str(self.entryfanoutflows) self.properties['exitfanoutflows'] = str(self.exitfanoutflows) self.properties['hostsites'] = str(self.hostsites) MultiPointVPNServiceFactory.getVpnService().saveResource(self)
def saveVPN(self): self.properties['vid'] = self.vid self.properties['priority'] = self.priority self.properties['meter'] = self.meter self.properties['mat'] = str(self.mat.serialize()) if not 'pops' in self.properties: self.properties['pops'] = {} for p in self.pops: self.properties['pops'][p] = p #self.properties['pops'] = tmp self.properties['vpnsites'] = str(self.vpnsites) self.properties['vpnsitevlans'] = str(self.vpnsitevlans) self.properties['entryfanoutflows'] = str(self.entryfanoutflows) self.properties['exitfanoutflows'] = str(self.exitfanoutflows) self.properties['hostsites'] = str(self.hostsites) MultiPointVPNServiceFactory.getVpnService().saveResource(self)
def addpop(self, pop): rc = True # See if we've already added the POP if pop.resourceName in self.pops: return False with self.lock: fhlist = [] # List of FlowHandles for this POP # We need to make sure that meter(s) are set correctly on the switches in the POP. # In particular we need to do this on Corsas before pushing flows to it that reference # any of the meters we use here. This code is a bit of a hard-coded hack, but it'll # have to do until we can figure out what's the desired behavior. Note that we can # set a meter multiple times. It is however a requirement that the driver needs to # have a set a meter before a flow references it; in particular we cannot use an # external mechanism (e.g. CLI) to set the meter and then try to have the driver # push a flow that references it. sw = Container.fromAnchor(pop.properties['HwSwitch']) meter = self.meter rc = setmeter(sw, meter, 0, 0, 0, 0) # if not rc: # Iterate over the existing POPs to set up entries to handle broadcast traffic # between the POP being added and the existing POPs. These all go in the # hardware switches. broadcastmac = "FF:FF:FF:FF:FF:FF" broadcastmac_mat = broadcastmac if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcastmac_mat = self.generateBroadcastMAC() for (remotepopname, remotepop) in self.pops.items(): vc = self.interconnectpops(pop.getResourceName(), remotepop.getResourceName()) fhs = swconnect(pop, remotepop, broadcastmac_mat, vc, self.meter) self.popflows[remotepop.getResourceName()].extend(fhs) fhlist.extend(fhs) # This POP is now added. self.pops[pop.resourceName] = pop self.popflows[pop.resourceName] = fhlist # Regenerate entry fanout flows for various hosts because they need to learn # about a new POP for (hostmac, hostsitename) in self.hostsites.items(): hostsite = self.vpnsites[hostsitename] hostvlan = self.vpnsitevlans[hostsitename] localpop = self.pops[hostsite['pop']] fh = self.makeentryfanoutflows(localpop=localpop, hostmac=hostmac, hostvlan=hostvlan, hostsite=hostsite) if fh != None: self.hostflows[hostmac].append(fh) # print "addpop ending with pop flow handles", self.popflows[pop.name] return rc
def interconnectpops(self, pop1,pop2): # XXX core topo! """ Given two POPs, return a GRI to be used to VPN traffic between them. Assumes that the first applicable GRI is the one to be used. :param pop1: Name of POP :param pop2: Name of POP :return: Link (Resource) """ vc = MultiPointVPNServiceFactory.getVpnService().coretopology.loadResource(pop1 + "--" + pop2) return vc
def interconnectpops(self, pop1, pop2): # XXX core topo! """ Given two POPs, return a GRI to be used to VPN traffic between them. Assumes that the first applicable GRI is the one to be used. :param pop1: Name of POP :param pop2: Name of POP :return: Link (Resource) """ vc = MultiPointVPNServiceFactory.getVpnService( ).coretopology.loadResource(pop1 + "--" + pop2) return vc
def makeentryfanoutflows(self, localpop, hostmac, hostvlan, hostsite): # Create entry fanout flow on the software switch. This flow fans out traffic # from the host/site to other hosts/sites on the same POP, as well as to all the # other POPs. # XXX need to re-run this part if we add another host/site to this POP or add # another POP forwards = [] broadcast_mat = "FF:FF:FF:FF:FF:FF" if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcast_mat = self.generateBroadcastMAC() # Locate all other sites on this POP. For each of these, make a forwarding entry to it. for (othersitename, othersite) in self.vpnsites.items(): if othersite[ 'pop'] == localpop.resourceName and othersitename != hostsite[ 'name']: fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectentryfanout fwd.vlan = int(self.vpnsitevlans[othersitename]) fwd.dstMac = broadcast_mat forwards.append(fwd) # Locate other POPs for (otherpopname, otherpop) in self.pops.items(): if otherpopname != localpop.resourceName: vc = self.interconnectpops(localpop.resourceName, otherpopname) core = Container.fromAnchor(localpop.properties['CoreRouter']) coreresname = core.resourceName (corename, coredom, coreport, corevlan) = getvcnode(vc, coreresname) fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectentryfanout fwd.vlan = int(corevlan) fwd.dstMac = broadcast_mat forwards.append(fwd) # If we've already made an entry fanout flow for this host, then delete it. if hostmac in self.entryfanoutflows: oldfh = self.entryfanoutflows[hostmac] if oldfh.isValid(): deleteforward(oldfh) del self.entryfanoutflows[hostmac] oldfh.invalidate() fh = connectentryfanoutmac(localpop=localpop, hostmac=hostmac, hostvlan=hostvlan, forwards=forwards, meter=self.meter, mac=broadcast_mat) if fh != None: self.entryfanoutflows[hostmac] = fh return fh
def addpop(self,pop): rc = True # See if we've already added the POP if pop.resourceName in self.pops: return False with self.lock: fhlist = [] # List of FlowHandles for this POP # We need to make sure that meter(s) are set correctly on the switches in the POP. # In particular we need to do this on Corsas before pushing flows to it that reference # any of the meters we use here. This code is a bit of a hard-coded hack, but it'll # have to do until we can figure out what's the desired behavior. Note that we can # set a meter multiple times. It is however a requirement that the driver needs to # have a set a meter before a flow references it; in particular we cannot use an # external mechanism (e.g. CLI) to set the meter and then try to have the driver # push a flow that references it. sw = Container.fromAnchor(pop.properties['HwSwitch']) meter = self.meter rc = setmeter(sw, meter, 0, 0, 0, 0) # if not rc: # Iterate over the existing POPs to set up entries to handle broadcast traffic # between the POP being added and the existing POPs. These all go in the # hardware switches. broadcastmac = "FF:FF:FF:FF:FF:FF" broadcastmac_mat = broadcastmac if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcastmac_mat = self.generateBroadcastMAC() for (remotepopname, remotepop) in self.pops.items(): vc = self.interconnectpops(pop.getResourceName(), remotepop.getResourceName()) fhs = swconnect(pop, remotepop, broadcastmac_mat, vc, self.meter) self.popflows[remotepop.getResourceName()].extend(fhs) fhlist.extend(fhs) # This POP is now added. self.pops[pop.resourceName] = pop self.popflows[pop.resourceName] = fhlist # Regenerate entry fanout flows for various hosts because they need to learn # about a new POP for (hostmac, hostsitename) in self.hostsites.items(): hostsite = self.vpnsites[hostsitename] hostvlan = self.vpnsitevlans[hostsitename] localpop = self.pops[hostsite['pop']] fh = self.makeentryfanoutflows(localpop= localpop, hostmac= hostmac, hostvlan= hostvlan, hostsite= hostsite) if fh != None: self.hostflows[hostmac].append(fh) # print "addpop ending with pop flow handles", self.popflows[pop.name] return rc
def makeentryfanoutflows(self, localpop, hostmac, hostvlan, hostsite): # Create entry fanout flow on the software switch. This flow fans out traffic # from the host/site to other hosts/sites on the same POP, as well as to all the # other POPs. # XXX need to re-run this part if we add another host/site to this POP or add # another POP forwards = [] broadcast_mat = "FF:FF:FF:FF:FF:FF" if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcast_mat = self.generateBroadcastMAC() # Locate all other sites on this POP. For each of these, make a forwarding entry to it. for (othersitename, othersite) in self.vpnsites.items(): if othersite['pop'] == localpop.resourceName and othersitename != hostsite['name']: fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectentryfanout fwd.vlan = int(self.vpnsitevlans[othersitename]) fwd.dstMac = broadcast_mat forwards.append(fwd) # Locate other POPs for (otherpopname, otherpop) in self.pops.items(): if otherpopname != localpop.resourceName: vc = self.interconnectpops(localpop.resourceName, otherpopname) core = Container.fromAnchor(localpop.properties['CoreRouter']) coreresname = core.resourceName (corename, coredom, coreport, corevlan) = getvcnode(vc, coreresname) fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectentryfanout fwd.vlan = int(corevlan) fwd.dstMac = broadcast_mat forwards.append(fwd) # If we've already made an entry fanout flow for this host, then delete it. if hostmac in self.entryfanoutflows: oldfh = self.entryfanoutflows[hostmac] if oldfh.isValid(): deleteforward(oldfh) del self.entryfanoutflows[hostmac] oldfh.invalidate() fh = connectentryfanoutmac(localpop= localpop, hostmac= hostmac, hostvlan= hostvlan, forwards= forwards, meter= self.meter, mac= broadcast_mat) if fh != None: self.entryfanoutflows[hostmac] = fh return fh
def shutdown(self): self.saveThread.stop() if self.sccThread != None: self.sccThread.stop() self.SCC.clearCallback() MultiPointVPNServiceFactory.delete()
def createService(sid): MultiPointVPNServiceFactory.create(None) vpnService = MultiPointVPNServiceFactory.getVpnService() vpnService.sid = sid vpnService.saveService() return vpnService
def packetInCallback(self, dpid, inPort, payload): """ Receive a PACKET_IN callback """ # Decode the callback. First get the switch switch = None hexdpid = binascii.hexlify(dpid) if hexdpid in self.switchIndex.keys(): switch = self.switchIndex[hexdpid] if switch == None: self.logger.error("Can't find switch " + str(dpid)) return # Now find the port if inPort not in switch.properties['Ports'].keys(): self.logger.error("Can't find port " + inPort + " on switch " + switch.resourceName) return port = Container.fromAnchor(switch.properties['Ports'][inPort]) frame = EthernetFrame.packetToFrame(payload) if frame == None: self.logger.error("Cannot parse Ethernet frame") return switchpopname = switch.properties['Pop'] # Log the packet we got self.logger.info("VpnCallback decode switch " + switch.resourceName + " (" + switchpopname + ") " + " port " + inPort + " vlan " + str(frame.getVid()) + " src " + EthernetFrame.byteString(frame.getSrcMac()) + " dst " + EthernetFrame.byteString(frame.getDstMac()) + " etherType " + hex(frame.getEtherType())) # Ignore some packets if frame.getEtherType() == EthernetFrame.ETHERTYPE_LLDP: self.logger.debug("LLDP frame ignored") return # Figure out the slice/service ID. This comes from the mapped destination address # (going to be a broadcast address). If it doesn't match our slice ID then drop. # XXX need to check this, not clear if the MACAddress constructor will DTRT. mac = MACAddress(frame.getDstMac()) if (mac.getSid() != self.vpnService.sid): self.logger.debug("Destination address doesn't match, ignored") return # Figure out which VPN (if any) this belongs to vpn = None vpnsite = None # Iterate over all VPNs then all site attachments. # If we can match the POP and VLAN, then we've got a match for the site attachments # XXX There is probably a more efficient way to do this. # XXX Note we can't do any port-based matching because all of the traffic from the # hardware switch to the software switch shows up on the same port on the software # switch, which is the one generating the PACKET_IN message. for (x,v) in MultiPointVPNServiceFactory.getVpnService().vpnIndex.items(): for (sitename,site) in v.vpnsites.items(): if site['pop'] == switchpopname and int(v.vpnsitevlans[sitename]) == frame.getVid(): vpn = v vpnsite = site if vpn == None: self.logger.error("Unable to find VPN or site for inbound packet") return # MAC layer address. For some reason we don't understand, this needs to be converted from # unicode (?!?) to ASCII before we can really use it despite the fact these are all ASCII # characters. mac = EthernetFrame.byteString(frame.getSrcMac()).encode('ascii', 'ignore') self.logger.info(" Source vpn " + vpn.name + " site " + vpnsite['name'] + " src " + mac) if vpn.addhostbymac(vpnsite, mac): self.logger.info("Added host successfully") else: self.logger.error("Adding host failed") return
def addhostbymac(self,hostsite,hostmac): """ Add a host by its MAC address :param hostsite: site descriptor structure (see global sites) :param hostmac: MAC address (string) :return: boolean True if successful """ # Normalize MAC address to lower-case hostmac = hostmac.lower() with self.lock: # See if the host already exists if hostmac in self.hostsites.keys(): # See if we're trying to add to the same site. If so that might indicate a problem # with flows, since we shouldn't be getting PACKET_IN events for a host after it's # been added. # # If the host shows up at a different site, maybe it moved? Not sure how to handle this # but for now take this as a failure. We could try to delete the host from the old site # and add it at the new site. # # XXX This condition might change after we add NFV support if hostsite['name'] == self.hostsites[hostmac]: self.logger.info("Host " + hostmac + " already exists at site " + hostsite['name']) return True else: self.logger.info("Host " + hostmac + " moved from site " + self.hostsites[hostmac] + " to site " + hostsite['name']) return False vlan = self.vpnsitevlans[hostsite['name']] # get VLAN from the site attachment localpopname = hostsite['pop'] localpop = self.pops[localpopname] host_mat = None broadcast_mat = "FF:FF:FF:FF:FF:FF" vpnService = MultiPointVPNServiceFactory.getVpnService(); if vpnService.properties['mat']: host_mat = self.generateMAC2(hostmac) self.hostflows[hostmac] = [] # Install flows for unicast traffic # Iterate over all other hosts in the VPN, set up pairwise flows to new host for (remotehostmac, remotesitename) in self.hostsites.items(): # If the host is at this site, don't need to do anything if remotesitename == hostsite['name']: continue remotesite = sites[remotesitename] vc = self.interconnect(hostsite, remotesite) remotevlan = self.vpnsitevlans[remotesite['name']] remotepop = self.pops[remotesite['pop']] remotehost_mat = None if vpnService.properties['mat']: remotehost_mat = self.generateMAC2(remotehostmac) fhlist = [] # Add flows coming from other site/host fhs = connectgrimac(topology= vpnService.topology, hostmac= hostmac, siteport= hostsite['hwport'], sitevlan= vlan, sitepop= localpop, remotesiteport= remotesite['hwport'], remotesitevlan= remotevlan, vc= vc, meter= self.meter, host_rewritemac= host_mat) if fhs != None: fhlist.extend(fhs) # Add flows going to other site/host fhs = connectgrimac(topology= vpnService.topology, hostmac= remotehostmac, siteport= remotesite['hwport'], sitevlan= remotevlan, sitepop= remotepop, remotesiteport= hostsite['hwport'], remotesitevlan= vlan, vc= vc, meter= self.meter, host_rewritemac= remotehost_mat) if fhs != None: fhlist.extend(fhs) self.hostflows[hostmac].extend(fhlist) self.hostflows[remotehostmac].extend(fhlist) fh = self.makeentryfanoutflows(localpop, hostmac, vlan, hostsite) if fh != None: self.hostsites[hostmac] = hostsite['name'] self.hostflows[hostmac].append(fh) # print "addhost ending with host flow handles", self.hostflows[host['name']] return True
def addsite(self,site,vlan): # If we've already added this site, don't do it again. # Note this is actually not a real requirement. In theory it should be possible # to put multiple attachments to a single site, on different VLANs. However # our implementation doesn't support that at this point, primarily because # we have some assumptions that each site only attaches once to a VPN. This check # here is mostly to avoid violating these assumptions and getting us in trouble. if site['name'] in self.vpnsites: return False # Make sure the pop to which the site is attached is already a part of the VPN. # In theory we could implicitly add a POP whenever we add a site that needs it. if site['pop'].lower() not in self.pops: return False self.vpnsites[site['name']] = site self.vpnsitevlans[site['name']] = vlan self.siteflows[site['name']] = [] broadcast_mat = "FF:FF:FF:FF:FF:FF" if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcast_mat = self.generateBroadcastMAC() # Add flows to get broadcast traffic between the site and the software switch. pop = self.pops[site['pop']] fhs = connecthostbroadcast(localpop= pop, hwport_tosite= site['hwport'], sitevlan= vlan, meter= self.meter, broadcast_rewritemac= broadcast_mat) if fhs == None: return False self.siteflows[site['name']].extend(fhs) # Create exit fanout flows on the software switch. This flow fans out traffic # from other POPs, to hosts/sites on this POP. # XXX need to re-run this part if we add another host/site to this POP or # add another POP # XXX Note that for a given port, the exit fanout flows are all identical, # except that their match VLAN numbers are different, corresponding to the VLAN # of the core OSCARS circuits. We might possibly be able to collapse these down # to a single flow rule if we don't try to match on the VLAN tag. It's not clear # if we want to do this or not, there might be some security and/or reliability # implications to doing this change. localpop = pop localpopname = localpop.resourceName forwards = [] if localpopname in self.exitfanoutflows: exitfanoutflows = self.exitfanoutflows[localpopname] else: exitfanoutflows = {} self.exitfanoutflows[localpopname] = exitfanoutflows # Locate all sites on this POP, including the one we're adding now. # Be ready to forward to their for (site2name, site2) in self.vpnsites.items(): if (site2['pop'] == site['pop']): fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectexitfanout fwd.vlan = int(self.vpnsitevlans[site2name]) fwd.dstMac = broadcast_mat forwards.append(fwd) # Iterate over source POPs for (srcpopname, srcpop) in self.pops.items(): if srcpopname != localpopname: # Get the VLAN coming from the core for this source POP vc = self.interconnectpops(localpopname, srcpopname) core = Container.fromAnchor(localpop.properties['CoreRouter']) coreresname = core.resourceName (corename, coredom, coreport, corevlan) = getvcnode(vc, coreresname) # If we already made an exit fanout flow for this source POP, delete it if srcpopname in exitfanoutflows: oldfh = exitfanoutflows[srcpopname] if oldfh.isValid(): deleteforward(oldfh) del exitfanoutflows[srcpopname] oldfh.invalidate() fh = connectexitfanout(localpop= pop, corevlan= corevlan, forwards= forwards, meter= self.meter, mac= broadcast_mat) if fh != None: exitfanoutflows[srcpopname] = fh self.popflows[srcpopname].append(fh) # Regenerate entry fanout flows for various hosts on this POP because they just # learned a new site. for (hostmac, hostsitename) in self.hostsites.items(): hostsite = self.vpnsites[hostsitename] if hostsite['pop'] == localpopname: hostvlan = self.vpnsitevlans[hostsitename] localpop = self.pops[hostsite['pop']] fh = self.makeentryfanoutflows(localpop= localpop, hostmac= hostmac, hostvlan= hostvlan, hostsite= hostsite) if fh != None: self.hostflows[hostmac].append(fh) return True
def addsite(self, site, vlan): # If we've already added this site, don't do it again. # Note this is actually not a real requirement. In theory it should be possible # to put multiple attachments to a single site, on different VLANs. However # our implementation doesn't support that at this point, primarily because # we have some assumptions that each site only attaches once to a VPN. This check # here is mostly to avoid violating these assumptions and getting us in trouble. if site['name'] in self.vpnsites: return False # Make sure the pop to which the site is attached is already a part of the VPN. # In theory we could implicitly add a POP whenever we add a site that needs it. if site['pop'].lower() not in self.pops: return False self.vpnsites[site['name']] = site self.vpnsitevlans[site['name']] = vlan self.siteflows[site['name']] = [] broadcast_mat = "FF:FF:FF:FF:FF:FF" if MultiPointVPNServiceFactory.getVpnService().properties['mat']: broadcast_mat = self.generateBroadcastMAC() # Add flows to get broadcast traffic between the site and the software switch. pop = self.pops[site['pop']] fhs = connecthostbroadcast(localpop=pop, hwport_tosite=site['hwport'], sitevlan=vlan, meter=self.meter, broadcast_rewritemac=broadcast_mat) if fhs == None: return False self.siteflows[site['name']].extend(fhs) # Create exit fanout flows on the software switch. This flow fans out traffic # from other POPs, to hosts/sites on this POP. # XXX need to re-run this part if we add another host/site to this POP or # add another POP # XXX Note that for a given port, the exit fanout flows are all identical, # except that their match VLAN numbers are different, corresponding to the VLAN # of the core OSCARS circuits. We might possibly be able to collapse these down # to a single flow rule if we don't try to match on the VLAN tag. It's not clear # if we want to do this or not, there might be some security and/or reliability # implications to doing this change. localpop = pop localpopname = localpop.resourceName forwards = [] if localpopname in self.exitfanoutflows: exitfanoutflows = self.exitfanoutflows[localpopname] else: exitfanoutflows = {} self.exitfanoutflows[localpopname] = exitfanoutflows # Locate all sites on this POP, including the one we're adding now. # Be ready to forward to their for (site2name, site2) in self.vpnsites.items(): if (site2['pop'] == site['pop']): fwd = SdnControllerClientL2Forward() fwd.outPort = "0" # to be filled in by connectexitfanout fwd.vlan = int(self.vpnsitevlans[site2name]) fwd.dstMac = broadcast_mat forwards.append(fwd) # Iterate over source POPs for (srcpopname, srcpop) in self.pops.items(): if srcpopname != localpopname: # Get the VLAN coming from the core for this source POP vc = self.interconnectpops(localpopname, srcpopname) core = Container.fromAnchor(localpop.properties['CoreRouter']) coreresname = core.resourceName (corename, coredom, coreport, corevlan) = getvcnode(vc, coreresname) # If we already made an exit fanout flow for this source POP, delete it if srcpopname in exitfanoutflows: oldfh = exitfanoutflows[srcpopname] if oldfh.isValid(): deleteforward(oldfh) del exitfanoutflows[srcpopname] oldfh.invalidate() fh = connectexitfanout(localpop=pop, corevlan=corevlan, forwards=forwards, meter=self.meter, mac=broadcast_mat) if fh != None: exitfanoutflows[srcpopname] = fh self.popflows[srcpopname].append(fh) # Regenerate entry fanout flows for various hosts on this POP because they just # learned a new site. for (hostmac, hostsitename) in self.hostsites.items(): hostsite = self.vpnsites[hostsitename] if hostsite['pop'] == localpopname: hostvlan = self.vpnsitevlans[hostsitename] localpop = self.pops[hostsite['pop']] fh = self.makeentryfanoutflows(localpop=localpop, hostmac=hostmac, hostvlan=hostvlan, hostsite=hostsite) if fh != None: self.hostflows[hostmac].append(fh) return True
def main(): vpnService = MultiPointVPNServiceFactory.getVpnService() try: command = sys.argv[1].lower() if command == 'help': usage() return if command == "startup": if vpnService == None: print "failed: the VPN service has not been created yet." return elif command == "init": vpnService = createService(int(sys.argv[2])) return if command == 'create': vpnname = sys.argv[2] vpn = vpnService.createVPN(vpnname) if vpn == None: print "vpn %r exists already" % vpnname else: print "VPN %s is created successfully." % vpn.name elif command == 'delete': vpnname = sys.argv[2] res = vpnService.deleteVPN(vpnname) if res: print "VPN %s removed successfully." % (vpnname) else: print "vpn name %s not found" % vpnname elif command == 'load': vpnService.loadService() elif command == "save": vpnService.saveService() elif command == "mat": if 'on' in sys.argv: vpnService.properties['mat'] = True if 'off' in sys.argv: vpnService.properties['mat'] = False state = {True:'on',False:'off'} print "MAC Address Translation feature is",state[vpnService.properties['mat']] elif command == "shutdown": vpnService.shutdown() elif command == "logging": logname = sys.argv[2] logging.basicConfig(format='%(asctime)s %(levelname)8s %(message)s', filename=logname, filemode='a', level=logging.INFO) logging.info("test") elif command == 'settopos': popstoponame = sys.argv[2] coretoponame = sys.argv[3] vpnService.settopos(popstoponame, coretoponame) elif command == 'listvpns': print "%20s" % ("VPN") for name in vpnService.vpnIndex: print "%20s" % name else: vpn = vpnService.getVPN(sys.argv[1]) command = sys.argv[2].lower() if command == 'getprio': prio = vpn.getpriority() print prio elif command == 'setprio': vpn.setpriority(sys.argv[3]) elif command == 'addpop': pop = vpnService.topology.loadResource(sys.argv[3]) if pop == None: print "unknown SDN POP" return res = vpnService.addPOP(vpn, pop) if res: print "POP %s has been added into VPN %s successfully." % (pop.resourceName, vpn.name) else: print "Error while adding pop." elif command == 'delpop': pop = vpnService.topology.loadResource(sys.argv[3]) if pop == None: print "unknown SDN POP" return res = vpnService.deletePOP(vpn, pop) if res: print "POP %s has been removed from VPN %s successfully." % (pop.resourceName, vpn.name) else: print "Error while deleting pop." elif command == 'addsite': site = vpnService.getSite(sys.argv[3]) if site == None: print "unknown site" return vlan = sys.argv[5] res = vpnService.addSite(vpn, site, vlan) if res: print "Site %s has been added into VPN %s successfully." % (site['name'], vpn.name) else: print "Error while adding site %s to VPN %s ." % (site['name'], vpn.name) elif command == 'delsite': site = vpnService.getSite(sys.argv[3]) if site == None: print "unknown site" return res = vpnService.deleteSite(vpn, site) if res: print "Site %s has been removed from VPN %s successfully." % (site['name'], vpn.name) else: print "could not delete site" elif command == 'addhostbymac': sitename = sys.argv[3] if sitename not in vpn.vpnsites: print "unknown site" return site = vpn.vpnsites[sitename] mac = sys.argv[5] vpnService.addhostbymac(vpn, site, mac) elif command == 'delhostbymac': mac = sys.argv[3] vpnService.delhostbymac(vpn, mac) elif command == 'listhosts': print "%17s %17s %s" % ("MAC", "Translated MAC", "Site") for (hostmac, hostsite) in vpn.hostsites.items(): hostmacmat = hostmac if vpnService.properties['mat']: hostmacmat = vpn.generateMAC2(hostmac) print "%17s %17s %s" % (hostmac, hostmacmat, hostsite) elif command == 'listsites': print "%6s %6s %20s %8s %4s" % ("Site", "POP", "Switch", "Port", "VLAN") for (sitename, site) in vpn.vpnsites.items(): popname = site['pop'] # string pop = Container.fromAnchor(vpnService.topology.properties['Pops'][popname]) hwswitch = Container.fromAnchor(pop.properties['HwSwitch']) hwswitchname = hwswitch.resourceName hwport = site['hwport'] vlan = vpn.vpnsitevlans[sitename] print "%6s %6s %20s %8s %4s" % (sitename, site['pop'], hwswitchname, hwport, vlan) elif command == 'listpops': print "%6s %20s %20s %20s" % ("POP", "Core Router", "Hardware Switch", "Software Switch") for (popname, pop) in vpn.pops.items(): if BootStrap.getBootStrap().getDataBase() != None: coreRouterName = pop.properties['CoreRouter']['resourceName'] hwSwitchName = pop.properties['HwSwitch']['resourceName'] swSwitchName = pop.properties['SwSwitch']['resourceName'] else: coreRouterName = pop.props['coreRouter'] hwSwitchName = pop.props['hwSwitch'] swSwitchName = pop.props['swSwitch'] print "%6s %20s %20s %20s" % (popname, coreRouterName, hwSwitchName, swSwitchName) else: print "unknown command" usage() except: # print "Invalid arguments" # usage() raise
def addhostbymac(self, hostsite, hostmac): """ Add a host by its MAC address :param hostsite: site descriptor structure (see global sites) :param hostmac: MAC address (string) :return: boolean True if successful """ # Normalize MAC address to lower-case hostmac = hostmac.lower() with self.lock: # See if the host already exists if hostmac in self.hostsites.keys(): # See if we're trying to add to the same site. If so that might indicate a problem # with flows, since we shouldn't be getting PACKET_IN events for a host after it's # been added. # # If the host shows up at a different site, maybe it moved? Not sure how to handle this # but for now take this as a failure. We could try to delete the host from the old site # and add it at the new site. # # XXX This condition might change after we add NFV support if hostsite['name'] == self.hostsites[hostmac]: self.logger.info("Host " + hostmac + " already exists at site " + hostsite['name']) return True else: self.logger.info("Host " + hostmac + " moved from site " + self.hostsites[hostmac] + " to site " + hostsite['name']) return False vlan = self.vpnsitevlans[ hostsite['name']] # get VLAN from the site attachment localpopname = hostsite['pop'] localpop = self.pops[localpopname] host_mat = None broadcast_mat = "FF:FF:FF:FF:FF:FF" vpnService = MultiPointVPNServiceFactory.getVpnService() if vpnService.properties['mat']: host_mat = self.generateMAC2(hostmac) self.hostflows[hostmac] = [] # Install flows for unicast traffic # Iterate over all other hosts in the VPN, set up pairwise flows to new host for (remotehostmac, remotesitename) in self.hostsites.items(): # If the host is at this site, don't need to do anything if remotesitename == hostsite['name']: continue remotesite = sites[remotesitename] vc = self.interconnect(hostsite, remotesite) remotevlan = self.vpnsitevlans[remotesite['name']] remotepop = self.pops[remotesite['pop']] remotehost_mat = None if vpnService.properties['mat']: remotehost_mat = self.generateMAC2(remotehostmac) fhlist = [] # Add flows coming from other site/host fhs = connectgrimac(topology=vpnService.topology, hostmac=hostmac, siteport=hostsite['hwport'], sitevlan=vlan, sitepop=localpop, remotesiteport=remotesite['hwport'], remotesitevlan=remotevlan, vc=vc, meter=self.meter, host_rewritemac=host_mat) if fhs != None: fhlist.extend(fhs) # Add flows going to other site/host fhs = connectgrimac(topology=vpnService.topology, hostmac=remotehostmac, siteport=remotesite['hwport'], sitevlan=remotevlan, sitepop=remotepop, remotesiteport=hostsite['hwport'], remotesitevlan=vlan, vc=vc, meter=self.meter, host_rewritemac=remotehost_mat) if fhs != None: fhlist.extend(fhs) self.hostflows[hostmac].extend(fhlist) self.hostflows[remotehostmac].extend(fhlist) fh = self.makeentryfanoutflows(localpop, hostmac, vlan, hostsite) if fh != None: self.hostsites[hostmac] = hostsite['name'] self.hostflows[hostmac].append(fh) # print "addhost ending with host flow handles", self.hostflows[host['name']] return True
def packetInCallback(self, dpid, inPort, payload): """ Receive a PACKET_IN callback """ # Decode the callback. First get the switch switch = None hexdpid = binascii.hexlify(dpid) if hexdpid in self.switchIndex.keys(): switch = self.switchIndex[hexdpid] if switch == None: self.logger.error("Can't find switch " + str(dpid)) return # Now find the port if inPort not in switch.properties['Ports'].keys(): self.logger.error("Can't find port " + inPort + " on switch " + switch.resourceName) return port = Container.fromAnchor(switch.properties['Ports'][inPort]) frame = EthernetFrame.packetToFrame(payload) if frame == None: self.logger.error("Cannot parse Ethernet frame") return switchpopname = switch.properties['Pop'] # Log the packet we got self.logger.info("VpnCallback decode switch " + switch.resourceName + " (" + switchpopname + ") " + " port " + inPort + " vlan " + str(frame.getVid()) + " src " + EthernetFrame.byteString(frame.getSrcMac()) + " dst " + EthernetFrame.byteString(frame.getDstMac()) + " etherType " + hex(frame.getEtherType())) # Ignore some packets if frame.getEtherType() == EthernetFrame.ETHERTYPE_LLDP: self.logger.debug("LLDP frame ignored") return # Figure out the slice/service ID. This comes from the mapped destination address # (going to be a broadcast address). If it doesn't match our slice ID then drop. # XXX need to check this, not clear if the MACAddress constructor will DTRT. mac = MACAddress(frame.getDstMac()) if (mac.getSid() != self.vpnService.sid): self.logger.debug("Destination address doesn't match, ignored") return # Figure out which VPN (if any) this belongs to vpn = None vpnsite = None # Iterate over all VPNs then all site attachments. # If we can match the POP and VLAN, then we've got a match for the site attachments # XXX There is probably a more efficient way to do this. # XXX Note we can't do any port-based matching because all of the traffic from the # hardware switch to the software switch shows up on the same port on the software # switch, which is the one generating the PACKET_IN message. for (x, v ) in MultiPointVPNServiceFactory.getVpnService().vpnIndex.items(): for (sitename, site) in v.vpnsites.items(): if site['pop'] == switchpopname and int( v.vpnsitevlans[sitename]) == frame.getVid(): vpn = v vpnsite = site if vpn == None: self.logger.error("Unable to find VPN or site for inbound packet") return # MAC layer address. For some reason we don't understand, this needs to be converted from # unicode (?!?) to ASCII before we can really use it despite the fact these are all ASCII # characters. mac = EthernetFrame.byteString(frame.getSrcMac()).encode( 'ascii', 'ignore') self.logger.info(" Source vpn " + vpn.name + " site " + vpnsite['name'] + " src " + mac) if vpn.addhostbymac(vpnsite, mac): self.logger.info("Added host successfully") else: self.logger.error("Adding host failed") return
def main(): vpnService = MultiPointVPNServiceFactory.getVpnService() try: command = sys.argv[1].lower() if command == 'help': usage() return if command == "startup": if vpnService == None: print "failed: the VPN service has not been created yet." return elif command == "init": vpnService = createService(int(sys.argv[2])) return if command == 'create': vpnname = sys.argv[2] vpn = vpnService.createVPN(vpnname) if vpn == None: print "vpn %r exists already" % vpnname else: print "VPN %s is created successfully." % vpn.name elif command == 'delete': vpnname = sys.argv[2] res = vpnService.deleteVPN(vpnname) if res: print "VPN %s removed successfully." % (vpnname) else: print "vpn name %s not found" % vpnname elif command == 'load': vpnService.loadService() elif command == "save": vpnService.saveService() elif command == "mat": if 'on' in sys.argv: vpnService.properties['mat'] = True if 'off' in sys.argv: vpnService.properties['mat'] = False state = {True: 'on', False: 'off'} print "MAC Address Translation feature is", state[ vpnService.properties['mat']] elif command == "shutdown": vpnService.shutdown() elif command == "logging": logname = sys.argv[2] logging.basicConfig( format='%(asctime)s %(levelname)8s %(message)s', filename=logname, filemode='a', level=logging.INFO) logging.info("test") elif command == 'settopos': popstoponame = sys.argv[2] coretoponame = sys.argv[3] vpnService.settopos(popstoponame, coretoponame) elif command == 'listvpns': print "%20s" % ("VPN") for name in vpnService.vpnIndex: print "%20s" % name else: vpn = vpnService.getVPN(sys.argv[1]) command = sys.argv[2].lower() if command == 'getprio': prio = vpn.getpriority() print prio elif command == 'setprio': vpn.setpriority(sys.argv[3]) elif command == 'addpop': pop = vpnService.topology.loadResource(sys.argv[3]) if pop == None: print "unknown SDN POP" return res = vpnService.addPOP(vpn, pop) if res: print "POP %s has been added into VPN %s successfully." % ( pop.resourceName, vpn.name) else: print "Error while adding pop." elif command == 'delpop': pop = vpnService.topology.loadResource(sys.argv[3]) if pop == None: print "unknown SDN POP" return res = vpnService.deletePOP(vpn, pop) if res: print "POP %s has been removed from VPN %s successfully." % ( pop.resourceName, vpn.name) else: print "Error while deleting pop." elif command == 'addsite': site = vpnService.getSite(sys.argv[3]) if site == None: print "unknown site" return vlan = sys.argv[5] res = vpnService.addSite(vpn, site, vlan) if res: print "Site %s has been added into VPN %s successfully." % ( site['name'], vpn.name) else: print "Error while adding site %s to VPN %s ." % ( site['name'], vpn.name) elif command == 'delsite': site = vpnService.getSite(sys.argv[3]) if site == None: print "unknown site" return res = vpnService.deleteSite(vpn, site) if res: print "Site %s has been removed from VPN %s successfully." % ( site['name'], vpn.name) else: print "could not delete site" elif command == 'addhostbymac': sitename = sys.argv[3] if sitename not in vpn.vpnsites: print "unknown site" return site = vpn.vpnsites[sitename] mac = sys.argv[5] vpnService.addhostbymac(vpn, site, mac) elif command == 'delhostbymac': mac = sys.argv[3] vpnService.delhostbymac(vpn, mac) elif command == 'listhosts': print "%17s %17s %s" % ("MAC", "Translated MAC", "Site") for (hostmac, hostsite) in vpn.hostsites.items(): hostmacmat = hostmac if vpnService.properties['mat']: hostmacmat = vpn.generateMAC2(hostmac) print "%17s %17s %s" % (hostmac, hostmacmat, hostsite) elif command == 'listsites': print "%6s %6s %20s %8s %4s" % ("Site", "POP", "Switch", "Port", "VLAN") for (sitename, site) in vpn.vpnsites.items(): popname = site['pop'] # string pop = Container.fromAnchor( vpnService.topology.properties['Pops'][popname]) hwswitch = Container.fromAnchor(pop.properties['HwSwitch']) hwswitchname = hwswitch.resourceName hwport = site['hwport'] vlan = vpn.vpnsitevlans[sitename] print "%6s %6s %20s %8s %4s" % ( sitename, site['pop'], hwswitchname, hwport, vlan) elif command == 'listpops': print "%6s %20s %20s %20s" % ( "POP", "Core Router", "Hardware Switch", "Software Switch") for (popname, pop) in vpn.pops.items(): if BootStrap.getBootStrap().getDataBase() != None: coreRouterName = pop.properties['CoreRouter'][ 'resourceName'] hwSwitchName = pop.properties['HwSwitch'][ 'resourceName'] swSwitchName = pop.properties['SwSwitch'][ 'resourceName'] else: coreRouterName = pop.props['coreRouter'] hwSwitchName = pop.props['hwSwitch'] swSwitchName = pop.props['swSwitch'] print "%6s %20s %20s %20s" % ( popname, coreRouterName, hwSwitchName, swSwitchName) else: print "unknown command" usage() except: # print "Invalid arguments" # usage() raise