void SimpleForwardingModule::handlePacketIn(const zmf::data::ZmfMessage& packetInMsg) {
    Device* destinationDevice = nullptr;
    // Unpack ZmfMessage which contains OpenFlow packet
    of_packet_in_t* ofPacketIn = zsdn::of_object_new_from_data_string_copy(packetInMsg.getData());
    of_octets_t ofPayload;
    of_packet_in_data_get(ofPacketIn, &ofPayload);
    // extracted message data
    uint8_t* payloadData = ofPayload.data;
    uint16_t payloadLength = ofPayload.bytes;
    // get the ethernet message frame
    Tins::EthernetII ethPacket(payloadData, payloadLength);
    of_version_t ofVersion = ofPacketIn->version;


    // some ethertype may be excluded from beeing handled by this module. we check if the received packet is one of them.
    bool ignoreEtherType = false;
    for (uint16_t ignoredType : ignoreEthertypes_) {
        if (ethPacket.payload_type() == ignoredType) {
            ignoreEtherType = true;
            break;
        }
    }
    if (ignoreEtherType) {
        getLogger().trace("Ignoring packet due to ethertype");
    }

    else {

        Tins::EthernetII::address_type dstAdress = ethPacket.dst_addr(); // extract the destination address

        uint64_t destinationMac = zsdn::NetUtils::mac_address_tins_to_uint64(dstAdress);
        // check for broadcast if == TRUE -> skip the message
        if (dstAdress.is_broadcast()) {
            this->getLogger().trace("Received Broadcast -> Message ignored");
        }

        else {

            this->getLogger().trace("Received Packet in");
            // check if the device is known if == FALSE -> request the device by a DeviceManager module
            if ((this->devices_.count(destinationMac) > 0)) {
                destinationDevice = &this->devices_.find(destinationMac)->second; // get the device out of the map
            } else {
                destinationDevice = this->requestDevice(destinationMac); // request the device by DeviceManager module
            }

            if (destinationDevice != nullptr) {
                // send the initial message payload to the destination switch port
                if (ofVersion == OF_VERSION_UNKNOWN) {
                    getLogger().warning("Received Packet with OF_VERSION_UNKNOWN. Ignoring Packet.");
                    return;
                }


                of_port_no_t port_no = destinationDevice->switchPort;



                of_object_t* resultObject = nullptr;
                switch (ofVersion) {

                    case OF_VERSION_UNKNOWN:
                        break;
                    case OF_VERSION_1_0:
                        of_packet_out_data_set(tempPacketOut_OF_1_0, &ofPayload);
                        of_action_output_port_set(tempOutPut_OF_1_0, port_no);
                        of_packet_out_actions_set(tempPacketOut_OF_1_0, tempActionList_OF_1_0);
                        resultObject = tempPacketOut_OF_1_0;
                        break;

                    case OF_VERSION_1_1:
                        break;
                    case OF_VERSION_1_2:
                        break;
                    case OF_VERSION_1_3:
                        of_packet_out_data_set(tempPacketOut_OF_1_3, &ofPayload);
                        of_action_output_port_set(tempOutPut_OF_1_3, port_no);
                        of_packet_out_actions_set(tempPacketOut_OF_1_3, tempActionList_OF_1_3);
                        resultObject = tempPacketOut_OF_1_3;
                        break;

                    case OF_VERSION_1_4:
                        break;
                }




                if (resultObject != nullptr) {
                    std::string messageBytes = zsdn::of_object_serialize_to_data_string(resultObject);

                    std::map<uint64_t, zmf::data::MessageType>::iterator msgType = this->linkDevicePacketOutMessageTypeMap_.find(
                            destinationDevice->macAddress);

                    this->getZmf()->publish(zmf::data::ZmfMessage(
                            msgType->second,
                            messageBytes));
                    if (this->getLogger().trace()) {
                        this->getLogger().trace(
                                "Forwarded packet to " + std::to_string(unsigned(destinationDevice->switchDpid)) + ":" +
                                std::to_string(unsigned(destinationDevice->switchPort)));
                    }
                }


            } else {
                this->getLogger().trace(
                        "Not able to forwared packet, host " + dstAdress.to_string() + " unknown");
            }
        }
    }
    of_packet_in_delete(ofPacketIn);
};
/*
 * icmp_packet_in_handler
 *
 * API for handling incoming packets
 */
indigo_core_listener_result_t
icmpa_packet_in_handler (of_packet_in_t *packet_in)
{
    of_octets_t                octets;
    of_port_no_t               port_no;
    of_match_t                 match;
    ppe_packet_t               ppep;
    indigo_core_listener_result_t result = INDIGO_CORE_LISTENER_RESULT_PASS;
    uint32_t                   type, code;

    debug_counter_inc(&pkt_counters.icmp_total_in_packets);
    if (!packet_in) return INDIGO_CORE_LISTENER_RESULT_PASS;

    of_packet_in_data_get(packet_in, &octets);

    /*
     * Identify the recv port
     */
    if (packet_in->version <= OF_VERSION_1_1) {
        return INDIGO_CORE_LISTENER_RESULT_PASS;
    } else {
        if (of_packet_in_match_get(packet_in, &match) < 0) {
            AIM_LOG_ERROR("ICMPA: match get failed");
            debug_counter_inc(&pkt_counters.icmp_internal_errors);
            return INDIGO_CORE_LISTENER_RESULT_PASS;
        }
        port_no = match.fields.in_port;
    }

    if (port_no == OF_PORT_DEST_CONTROLLER) {
        debug_counter_inc(&pkt_counters.icmp_total_passed_packets);
        return INDIGO_CORE_LISTENER_RESULT_PASS;
    }

    if (port_no > MAX_PORTS) {
        AIM_LOG_ERROR("ICMPA: Port No: %d Out of Range %d", port_no, MAX_PORTS);
        debug_counter_inc(&pkt_counters.icmp_internal_errors);
        return INDIGO_CORE_LISTENER_RESULT_PASS;
    }

    /*
     * Check the packet-in reasons in metadata
     *
     * Icmp agent should not consume packets coming in due to L2 Src miss
     * and Station Move.
     */
    if ((match.fields.metadata & OFP_BSN_PKTIN_FLAG_STATION_MOVE) ||
        (match.fields.metadata & OFP_BSN_PKTIN_FLAG_NEW_HOST)) {
        debug_counter_inc(&pkt_counters.icmp_total_passed_packets);
        return INDIGO_CORE_LISTENER_RESULT_PASS;
    }

    ppe_packet_init(&ppep, octets.data, octets.bytes);
    if (ppe_parse(&ppep) < 0) {
        AIM_LOG_RL_ERROR(&icmp_pktin_log_limiter, os_time_monotonic(),
                         "ICMPA: Packet_in parsing failed.");
        debug_counter_inc(&pkt_counters.icmp_internal_errors);
        return INDIGO_CORE_LISTENER_RESULT_PASS;
    }

    /*
     * Identify if this is an Echo Request, destined to one of VRouter
     */
    if (ppe_header_get(&ppep, PPE_HEADER_ICMP)) {
        if (icmpa_reply(&ppep, port_no, &result)) {
            ++port_pkt_counters[port_no].icmp_echo_packets;
            return result;
        }
    }

    /*
     * To handle traceroute, we need to check for
     * a) UDP Packet
     * b) dest IP is Vrouter IP
     * c) UDP src and dest ports are ephemeral
     */
    if (ppe_header_get(&ppep, PPE_HEADER_UDP) &&
        ppe_header_get(&ppep, PPE_HEADER_IP4)) {
        uint32_t dest_ip, src_port, dest_port;
        ppe_field_get(&ppep, PPE_FIELD_IP4_DST_ADDR, &dest_ip);
        ppe_field_get(&ppep, PPE_FIELD_UDP_SRC_PORT, &src_port);
        ppe_field_get(&ppep, PPE_FIELD_UDP_DST_PORT, &dest_port);

        if (router_ip_check(dest_ip) && is_ephemeral(src_port) &&
            is_ephemeral(dest_port)) {
            AIM_LOG_TRACE("ICMP Port Unreachable received on port: %d",
                          port_no);
            type = ICMP_DEST_UNREACHABLE;
            code = 3;
            result = INDIGO_CORE_LISTENER_RESULT_DROP;
            if (icmpa_send(&ppep, port_no, type, code)) {
                ++port_pkt_counters[port_no].icmp_port_unreachable_packets;
                return result;
            }
        }
    }

    /*
     * Identify if the reason is valid for ICMP Agent to consume the packet
     */
    if (match.fields.metadata & OFP_BSN_PKTIN_FLAG_L3_MISS) {
        AIM_LOG_TRACE("ICMP Dest Network Unreachable received on port: %d",
                      port_no);
        type = ICMP_DEST_UNREACHABLE;
        code = 0;
        result = INDIGO_CORE_LISTENER_RESULT_DROP;
        if (icmpa_send(&ppep, port_no, type, code)) {
            ++port_pkt_counters[port_no].icmp_net_unreachable_packets;
        }
    } else if (match.fields.metadata & OFP_BSN_PKTIN_FLAG_TTL_EXPIRED) {
        AIM_LOG_TRACE("ICMP TTL Expired received on port: %d", port_no);
        type = ICMP_TIME_EXCEEDED;
        code = 0;
        result = INDIGO_CORE_LISTENER_RESULT_DROP;
        if (icmpa_send(&ppep, port_no, type, code)) {
            ++port_pkt_counters[port_no].icmp_time_exceeded_packets;
        }
    }

    return result;
}