示例#1
0
文件: vpn.py 项目: modulexcite/enos
 def __init__(self,name):
     global VPNinstances, VPNindex, VPNlock
     with VPNlock:
         VPNinstances['name'] = self
         self.vid = VPNindex
         VPNindex += 1
     self.name = name
     self.pops = {}
     self.vpnsites = {}
     self.priority = "low"
     self.meter = 3
     self.lock = threading.Lock()
     self.mat = MAT(self.vid)
示例#2
0
文件: vpn.py 项目: cygmris/enos
    def __init__(self, name, vs):
        Resource.__init__(self, name, "net.es.netshell.api.Resource")
        self.vpnService = vs
        self.vid = 0
        self.name = name
        self.pops = {}  # pop name -> pop
        self.vpnsites = {}  # site name -> site
        self.vpnsitevlans = {}  # site name -> vlan tag for attachment
        self.popflows = {}  # pop name -> (FlowHandle)
        self.siteflows = {}  # site name -> (FlowHandle)
        self.hostflows = {}  # host name -> (FlowHandle)
        self.entryfanoutflows = {}  # host name -> FlowHandle
        self.exitfanoutflows = {
        }  # exit pop name -> (entry pop name, FlowHandle)
        self.hostsites = {}  # MAC address / host -> site name
        self.priority = "low"
        self.meter = 3
        self.lock = threading.Lock()
        self.mat = MAT(sid=self.vpnService.sid, vid=self.vid)

        self.logger = logging.getLogger("VPN")
        self.logger.setLevel(logging.INFO)
        self.setResourceType("VPN")
示例#3
0
文件: vpn.py 项目: cygmris/enos
    def loadVPN(self, mpvpn):
        stored = mpvpn.loadResource(self.getResourceName())
        mapResource(obj=self, resource=stored)

        self.name = self.getResourceName()
        self.vid = self.properties['vid']
        self.priority = self.properties['priority']
        self.meter = self.properties['meter']
        self.pops = {}
        for (n, p) in self.properties['pops'].items():
            self.pops[n] = mpvpn.topology.loadResource(n)
        self.vpnsites = eval(self.properties['vpnsites'])
        self.vpnsitevlans = eval(self.properties['vpnsitevlans'])
        self.exitfanoutflows = eval(self.properties['exitfanoutflows'])
        self.entryfanoutflows = eval(self.properties['entryfanoutflows'])
        self.hostsites = eval(self.properties['hostsites'])
        self.mat = MAT.deserialize(self.properties['mat'])

        self.vpnService = vpnService  # global
示例#4
0
文件: vpn.py 项目: esnet/enos
    def loadVPN(self,mpvpn):
        stored = mpvpn.loadResource(self.getResourceName())
        mapResource(obj=self,resource=stored)

        self.name = self.getResourceName()
        self.vid = self.properties['vid']
        self.priority = self.properties['priority']
        self.meter = self.properties['meter']
        self.pops={}
        for (n,p) in self.properties['pops'].items():
            self.pops[n] = mpvpn.topology.loadResource(n)
        self.vpnsites = eval (self.properties['vpnsites'])
        self.vpnsitevlans = eval (self.properties['vpnsitevlans'])
        self.exitfanoutflows = eval (self.properties['exitfanoutflows'])
        self.entryfanoutflows = eval (self.properties['entryfanoutflows'])
        self.hostsites = eval (self.properties['hostsites'])
        self.mat = MAT.deserialize(self.properties['mat'])

        self.vpnService = vpnService # global
示例#5
0
文件: vpn.py 项目: esnet/enos
    def __init__(self,name,vs):
        Resource.__init__(self,name,"net.es.netshell.api.Resource")
        self.vpnService = vs
        self.vid = 0
        self.name = name
        self.pops = {}              # pop name -> pop
        self.vpnsites = {}          # site name -> site
        self.vpnsitevlans = {}      # site name -> vlan tag for attachment
        self.popflows = {}          # pop name -> (FlowHandle)
        self.siteflows = {}         # site name -> (FlowHandle)
        self.hostflows = {}         # host name -> (FlowHandle)
        self.entryfanoutflows = {}  # host name -> FlowHandle
        self.exitfanoutflows = {}   # exit pop name -> (entry pop name, FlowHandle)
        self.hostsites = {}         # MAC address / host -> site name
        self.priority = "low"
        self.meter = 3
        self.lock = threading.Lock()
        self.mat = MAT(sid=self.vpnService.sid, vid=self.vid)

        self.logger = logging.getLogger("VPN")
        self.logger.setLevel(logging.INFO)
        self.setResourceType("VPN")
示例#6
0
文件: vpn.py 项目: modulexcite/enos
class VPN():

    def __init__(self,name):
        global VPNinstances, VPNindex, VPNlock
        with VPNlock:
            VPNinstances['name'] = self
            self.vid = VPNindex
            VPNindex += 1
        self.name = name
        self.pops = {}
        self.vpnsites = {}
        self.priority = "low"
        self.meter = 3
        self.lock = threading.Lock()
        self.mat = MAT(self.vid)

    def getsite(self,host):
        for (s,site) in self.vpnsites.items():
            if host['name'] in site['hosts']:
                return site
        return None
    def addpop(self,pop):
        self.pops[pop.name] = pop
        return True
    def delpop(self,pop):
        del self.pops[pop.name]
        return True
    def addsite(self,site):
        self.vpnsites[site['name']] = site
        return True
    def delsite(self,site):
        del self.vpnsites[site['name']]
        return True
    def generateMAC(self,host):
        interface = getdatapaths(host)[0]
        mac = interface['mac']
        # MAT.translate is idempotent: if a translated mac has already been allocated for a given mac, the
        # function will not generate a new mac but instead return the previously generated mac.
        newmac = self.mat.translate(mac)
        return str(newmac)

    def addhost(self,host,vlan):
        with self.lock:
            hostsite = self.getsite(host)
            hostsite['connected'][host['name']] = vlan
            for (s,site) in self.vpnsites.items():
                if site['name'] == hostsite['name']:
                    continue
                connected = site['connected']
                for (r,remotevlan) in connected.items():
                    gri = interconnect(hostsite,site)
                    remotehost = tbns[r]
                    host_mat = None
                    remotehost_mat = None
                    if VPNMAT:
                        host_mat = self.generateMAC(host)
                        remotehost_mat =  self.generateMAC(remotehost)
                    # Add flows coming from other sites
                    connectgri(host=host,
                               host_rewitemac = host_mat,
                               hostvlan=vlan,
                               remotehost=remotehost,
                               remotehostvlan = remotevlan,
                               gri=gri,meter=self.meter)
                    # Add flows going to other sites
                    connectgri(host=remotehost,
                               host_rewitemac = remotehost_mat,
                               hostvlan=remotevlan,
                               remotehost=host,
                               remotehostvlan=vlan,
                               gri=gri,
                               meter=self.meter)

        return True
    def delhost(self,host):
        return True

    def setpriority(self,priority):
        self.priority = priority
        if priority == 'high':
            self.meter = 5
        else:
            self.meter = 3

    def getpriority(self):
        return self.priority
示例#7
0
文件: vpn.py 项目: cygmris/enos
class VPN(Resource):
    def __init__(self, name, vs):
        Resource.__init__(self, name, "net.es.netshell.api.Resource")
        self.vpnService = vs
        self.vid = 0
        self.name = name
        self.pops = {}  # pop name -> pop
        self.vpnsites = {}  # site name -> site
        self.vpnsitevlans = {}  # site name -> vlan tag for attachment
        self.popflows = {}  # pop name -> (FlowHandle)
        self.siteflows = {}  # site name -> (FlowHandle)
        self.hostflows = {}  # host name -> (FlowHandle)
        self.entryfanoutflows = {}  # host name -> FlowHandle
        self.exitfanoutflows = {
        }  # exit pop name -> (entry pop name, FlowHandle)
        self.hostsites = {}  # MAC address / host -> site name
        self.priority = "low"
        self.meter = 3
        self.lock = threading.Lock()
        self.mat = MAT(sid=self.vpnService.sid, vid=self.vid)

        self.logger = logging.getLogger("VPN")
        self.logger.setLevel(logging.INFO)
        self.setResourceType("VPN")

    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 loadVPN(self, mpvpn):
        stored = mpvpn.loadResource(self.getResourceName())
        mapResource(obj=self, resource=stored)

        self.name = self.getResourceName()
        self.vid = self.properties['vid']
        self.priority = self.properties['priority']
        self.meter = self.properties['meter']
        self.pops = {}
        for (n, p) in self.properties['pops'].items():
            self.pops[n] = mpvpn.topology.loadResource(n)
        self.vpnsites = eval(self.properties['vpnsites'])
        self.vpnsitevlans = eval(self.properties['vpnsitevlans'])
        self.exitfanoutflows = eval(self.properties['exitfanoutflows'])
        self.entryfanoutflows = eval(self.properties['entryfanoutflows'])
        self.hostsites = eval(self.properties['hostsites'])
        self.mat = MAT.deserialize(self.properties['mat'])

        self.vpnService = vpnService  # global

    def interconnect(self, site1, site2):  # XXX core topo!
        """
        Given two sites, return a GRI to be used for VPN traffic between them.
        Assumes that the first applicable GRI is the one to be used.
        :param site1:
        :param site2:
        :return: Link (Resource)
        """
        pop1 = site1['pop']
        pop2 = site2['pop']
        return self.interconnectpops(pop1, pop2)

    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 delpop(self, pop):
        # Ideally we would undo the meter setting that was done in addpop.  But at this point
        # we don't have a mechanism for handling the fact that a meter might be in use by
        # flows for multiple VPNs.  If we indiscriminantly delete a meter, we might blow away
        # flows in use by some other, unrelated VPNs.  This is solely an issue on the Corsas
        # at this point.
        if pop.resourceName in self.popflows.keys():
            self.deletefhs(self.popflows[pop.resourceName])
            del self.popflows[pop.resourceName]
        del self.pops[pop.resourceName]

        # Regenerate entry fanout flows for various hosts because they don't need to fanout
        # to this POP anymore
        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)

        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 delsite(self, site):
        localpop = self.pops[site['pop']]
        localpopname = localpop.resourceName

        if site['name'] in self.siteflows.keys():
            self.deletefhs(self.siteflows[site['name']])
            del self.siteflows[site['name']]
        del self.vpnsites[site['name']]
        del self.vpnsitevlans[site['name']]

        # Regenerate entry fanout flows for various hosts on this POP because they need
        # to forget this 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 generateMAC2(self, mac):
        """
        Translate a MAC address.
        :param mac: MAC address to translate (intended to be a string but can be any valid type to MACaddress constructor
        :return: String
        """
        return str(self.mat.translate(mac))

    def generateBroadcastMAC(self):
        return str(self.mat.translate("FF:FF:FF:FF:FF:FF"))

    def deletefhs(self, fhs):
        for f in fhs:
            if f.isValid():
                deleteforward(f)
                f.invalidate()

    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 delhostbymac(self, hostmac):

        # Normalize MAC address to lower-case
        hostmac = hostmac.lower()

        if hostmac in self.hostsites.keys():
            del self.hostsites[hostmac]
        else:
            return False

        if hostmac in self.hostflows.keys():
            self.deletefhs(self.hostflows[hostmac])
            del self.hostflows[hostmac]
        return True

    def setpriority(self, priority):
        """
        Set the priority for this VPN's flows.
        Can be either "high" or low, but in the current implementation this must be
        set before any POPs, sites, or hosts are added.
        """
        self.priority = priority
        if priority == 'high':
            self.meter = 5
        else:
            self.meter = 3

    def getpriority(self):
        return self.priority
示例#8
0
文件: vpn.py 项目: esnet/enos
class VPN(Resource):
    def __init__(self,name,vs):
        Resource.__init__(self,name,"net.es.netshell.api.Resource")
        self.vpnService = vs
        self.vid = 0
        self.name = name
        self.pops = {}              # pop name -> pop
        self.vpnsites = {}          # site name -> site
        self.vpnsitevlans = {}      # site name -> vlan tag for attachment
        self.popflows = {}          # pop name -> (FlowHandle)
        self.siteflows = {}         # site name -> (FlowHandle)
        self.hostflows = {}         # host name -> (FlowHandle)
        self.entryfanoutflows = {}  # host name -> FlowHandle
        self.exitfanoutflows = {}   # exit pop name -> (entry pop name, FlowHandle)
        self.hostsites = {}         # MAC address / host -> site name
        self.priority = "low"
        self.meter = 3
        self.lock = threading.Lock()
        self.mat = MAT(sid=self.vpnService.sid, vid=self.vid)

        self.logger = logging.getLogger("VPN")
        self.logger.setLevel(logging.INFO)
        self.setResourceType("VPN")

    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 loadVPN(self,mpvpn):
        stored = mpvpn.loadResource(self.getResourceName())
        mapResource(obj=self,resource=stored)

        self.name = self.getResourceName()
        self.vid = self.properties['vid']
        self.priority = self.properties['priority']
        self.meter = self.properties['meter']
        self.pops={}
        for (n,p) in self.properties['pops'].items():
            self.pops[n] = mpvpn.topology.loadResource(n)
        self.vpnsites = eval (self.properties['vpnsites'])
        self.vpnsitevlans = eval (self.properties['vpnsitevlans'])
        self.exitfanoutflows = eval (self.properties['exitfanoutflows'])
        self.entryfanoutflows = eval (self.properties['entryfanoutflows'])
        self.hostsites = eval (self.properties['hostsites'])
        self.mat = MAT.deserialize(self.properties['mat'])

        self.vpnService = vpnService # global

    def interconnect(self, site1,site2): # XXX core topo!
        """
        Given two sites, return a GRI to be used for VPN traffic between them.
        Assumes that the first applicable GRI is the one to be used.
        :param site1:
        :param site2:
        :return: Link (Resource)
        """
        pop1 = site1['pop']
        pop2 = site2['pop']
        return self.interconnectpops(pop1,pop2)

    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 delpop(self,pop):
        # Ideally we would undo the meter setting that was done in addpop.  But at this point
        # we don't have a mechanism for handling the fact that a meter might be in use by
        # flows for multiple VPNs.  If we indiscriminantly delete a meter, we might blow away
        # flows in use by some other, unrelated VPNs.  This is solely an issue on the Corsas
        # at this point.
        if pop.resourceName in self.popflows.keys():
            self.deletefhs(self.popflows[pop.resourceName])
            del self.popflows[pop.resourceName]
        del self.pops[pop.resourceName]

        # Regenerate entry fanout flows for various hosts because they don't need to fanout
        # to this POP anymore
        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)

        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 delsite(self,site):
        localpop = self.pops[site['pop']]
        localpopname = localpop.resourceName

        if site['name'] in self.siteflows.keys():
            self.deletefhs(self.siteflows[site['name']])
            del self.siteflows[site['name']]
        del self.vpnsites[site['name']]
        del self.vpnsitevlans[site['name']]

        # Regenerate entry fanout flows for various hosts on this POP because they need
        # to forget this 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 generateMAC2(self,mac):
        """
        Translate a MAC address.
        :param mac: MAC address to translate (intended to be a string but can be any valid type to MACaddress constructor
        :return: String
        """
        return str(self.mat.translate(mac))
    def generateBroadcastMAC(self):
        return str(self.mat.translate("FF:FF:FF:FF:FF:FF"))

    def deletefhs(self, fhs):
        for f in fhs:
            if f.isValid():
                deleteforward(f)
                f.invalidate()

    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 delhostbymac(self,hostmac):

        # Normalize MAC address to lower-case
        hostmac = hostmac.lower()

        if hostmac in self.hostsites.keys():
            del self.hostsites[hostmac]
        else:
            return False

        if hostmac in self.hostflows.keys():
            self.deletefhs(self.hostflows[hostmac])
            del self.hostflows[hostmac]
        return True

    def setpriority(self,priority):
        """
        Set the priority for this VPN's flows.
        Can be either "high" or low, but in the current implementation this must be
        set before any POPs, sites, or hosts are added.
        """
        self.priority = priority
        if priority == 'high':
            self.meter = 5
        else:
            self.meter = 3

    def getpriority(self):
        return self.priority