def deserialize(doc): obj = eval(doc) mat = MAT(obj['sid'], obj['vid']) mat.props['hid'] = obj['hid'] for (hid, mac) in obj['mac'].items(): # Note: json file can only use str as key # so we need to transfer hid back to int first mat.props['mac'][int(hid)] = MACAddress(mac) return mat
def __init__(self, sid, vid): super(MAT, self).__init__(name='MAT[%d]' % vid) self.props['sid'] = sid self.props['vid'] = vid self.props['hid'] = {} # [str(mac)] = hid self.props['mac'] = {} # [hid] = mac self.props['lock'] = threading.Lock() self.props['mac'][0xFFFF] = MACAddress("FF:FF:FF:FF:FF:FF") self.props['hid']['FF:FF:FF:FF:FF:FF'] = 0xFFFF
def translate(self, mac): if not str(mac) in self.props['hid']: with self.props['lock']: if not str(mac) in self.props['hid']: hid = random.randint(1, MAT.reserved) while hid in self.props['mac']: hid = random.randint(1, MAT.reserved) self.props['hid'][str(mac)] = hid self.props['mac'][hid] = mac trans_mac = MACAddress() trans_mac.setSid(self.props['sid']) trans_mac.setVid(self.props['vid']) hid = self.props['hid'][str(mac)] trans_mac.setHid(hid) if hid > MAT.reserved: # broadcast MAC address trans_mac.data[0] = 0xFF return trans_mac
def deserialize(obj, net): mac = MACAddress(obj['mac']) vlan = obj['vlan'] port = net.builder.portIndex[obj['port']] return FlowEntry(mac, vlan, port)
def tryCallback(self, notification): """ And this is the callback itself. Everything that touches an ODL-specific data structure needs to go in here...above this layer it's all generic OpenFlow. :param notification org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived """ # Find out where the packet came from (ingress port). First find the node and connector # in the ODL/Java world (ingressNode and ingressNodeConnector) nodeConnectorRef = notification.getIngress() # XXX need to turn this into a NodeConnector object # String nodeId = ingress.getValue().firstIdentifierOf(Node.class).firstKeyOf(Node.class, NodeKey.class).getId().getValue(); ingressNodeId = nodeConnectorRef.getValue().firstIdentifierOf(Node).firstKeyOf(Node, NodeKey).getId().getValue() switch = self.getSwitch(ingressNodeId) # ENOS switch ingressNode = self.findODLSwitch(switch) # ODL switch corresponding to it # String ncId = ncRef.getValue().firstIdentifierOf(NodeConnector.class).firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(); ingressNodeConnectorId = nodeConnectorRef.getValue().firstIdentifierOf(NodeConnector).firstKeyOf(NodeConnector, NodeConnectorKey).getId().getValue() # Make sure this is an OpenFlow switch. If not, ignore the packet. if ingressNode.getAugmentation(FlowCapableNode) is not None: # This part is harder. Need to figure out the ENOS port from the # NodeConnector object. We also have the ENOS switch. # ingressNodeConnectorId is of the form u'openflow:72620962556436737:3', # but we need to get to an ENOS port from that (which has the form "eth13" # (for OVS) or "1" (for Corsa). The ENOS port name for # NodeConnectorId can be gotten from the FlowCapableNodeConnector # augmentation for the NodeConnector. # We iterate over the Node's connectors to try to find the correct # NodeConnector...it's sort of the opposite of OdlMdsalImpl.getNodeConnector # (and this implies that we should probably make this into a Java function # someday. # XXX Would replacing this with something that just reads the NodeConnector # from the data store be more efficient? It would avoid the loop, but by time # we get here we've already queried the data store for the Node anyway. ncs = self.odlMdsalImpl.getNodeConnectors(ingressNode) IngressNodeConnector = None portName = None for nc in ncs: if nc.getId().getValue() == ingressNodeConnectorId: IngressNodeConnector = nc fcnc = nc.getAugmentation(FlowCapableNodeConnector) portName = fcnc.getName() break # Complain if we can't figure out the ENOS port name if portName == None: ODLClient.logger.error("Can't determine port name for NodeConnector %r on node %r" % (ingressNodeConnectorId, ingressNodeId)) return if not portName in switch.props['ports']: ODLClient.logger.error("Can't find port %s on node %s" % (portName, switch)) return port = switch.props['ports'][portName] # Complain if we can't find the port, even though we have its name if port == None: ODLClient.logger.error("Can't find port %r on node %r" % (portName, ingressNodeId)) return if self.debug and not self.dropLLDP: print "PACKET_IN from port %r in node %r" % (port, ingressNodeId) # Try to decode the packet. First get the payload bytes and parse them. l2pkt = notification.getPayload() ethernetFrame = EthernetFrame.packetToFrame(l2pkt) if ethernetFrame != None: srcMac = MACAddress(ethernetFrame.getSrcMac()) destMac = MACAddress(ethernetFrame.getDstMac()) etherType = ethernetFrame.getEtherType() & 0xffff # convert to unsigned type vlanTag = ethernetFrame.getVid() payload = ethernetFrame.getPayload() # Possibly drop LLDP frames if self.dropLLDP: if etherType == EthernetFrame.ETHERTYPE_LLDP: return packetIn = PacketInEvent(inPort = port, srcMac=srcMac, dstMac=destMac, vlan=vlanTag, payload=payload) packetIn.props['ethertype'] = etherType if self.debug: print " Ethernet frame " + srcMac.str() + " -> " + destMac.str() + " Ethertype " + "%04x" % etherType + " VLAN " + "%04x" % vlanTag self.dispatchPacketIn(packetIn) else: if self.debug: print " Unknown frame type" else: if self.debug: print "PACKET_IN from Non-OpenFlow Switch"
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 reverse(self, hid): if not hid in self.props['mac']: return None return MACAddress(self.props['mac'][hid])
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