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 send(self, packet): """ Send a packet via a PACKET_OUT OpenFlow message. We use the PacketOut members and use net.es.netshell.odlmdsal.impl.EthernetFrame to help us put a VLAN header on the front :param packet: common.openflow.PacketOut :return: True if successful, False if not """ # print "ODLClient.send with packet from " + str(packet.dl_src) + " to " + str(packet.dl_dst) + " on " + packet.scope.switch.props['dpid'] if self.isPacketOutValid(packet): # Get the switch (Node in the ODL world) and port (NodeConnector in the ODL world) sw = self.findODLSwitch(packet.scope.switch) dpid = packet.scope.switch.props['dpid'][-8:] if sw == None: print packet, "cannot be sent because the switch is not in inventory" return False portName = packet.port.name nodeconn = self.odlController.getNodeConnector(sw, portName) if nodeconn == None: ODLClient.logger.warning('can not send %r because the port %s on %r is invalid' % (packet, portName, sw.getId())) return False # Create the outgoing packet in ODL land. The outgoing node connector must be set. frame = EthernetFrame() frame.setDstMac(self.javaByteArray(packet.dl_dst.data)) frame.setSrcMac(self.javaByteArray(packet.dl_src.data)) frame.setEtherType(packet.etherType) frame.setVid(packet.vlan) frame.setPayload(packet.payload) self.odlController.transmitDataPacket(self.javaByteArray(dpid), portName, frame.toPacket()) return True ODLClient.logger.warning("Packet %r is not valid" % packet) return False