def do_flow_mod(self, event=None):
        """
        If the event is not specified, then issues a flow mod with random src
        and dst ports; all the other fields will match against the trigger event
        saved earlier. Does not issue pkt_out.
        
        Otherwise, does a normal flow_mod.
        
        """
        msg = of.ofp_flow_mod()

        # Normal flow-mod
        if event:
            msg.match = of.ofp_match.from_packet(event.parse())
            msg.actions.append(of.ofp_action_output(port=get_the_other_port(event.port)))
            msg.buffer_id = event.ofp.buffer_id
            
            # Save the trigger event for later matching.
            if msg.match.tp_dst == TRIGGER_PORT:
                with self.lock:
                    self.trigger_event = event
                mylog('Received trigger event. Trigger event.parse() =', pretty_dict(dictify(event.parse())))
            
            mylog('Installed flow:', pretty_dict(dictify(msg.match)))
            
        # Special flow-mod that generates random source/dst ports.
        else:
            with self.lock:
                assert self.trigger_event
                trigger_packet = func_cache(self.trigger_event.parse)
                msg.match = of.ofp_match.from_packet(trigger_packet)
                msg.actions.append(of.ofp_action_output(port=get_the_other_port(self.trigger_event.port)))
            msg.match.tp_dst = random.randint(10, 65000)
            msg.match.tp_src = random.randint(10, 65000)
            
        current_time = time.time()
        with self.lock:
            (count, start, _) = self.flow_mod_stat
            if start is None: start = current_time
            self.flow_mod_stat = (count + 1, start, current_time)

        msg.idle_timeout = IDLE_TIMEOUT
        msg.hard_timeout = HARD_TIMEOUT

        if (not USE_LIMITER) or (USE_LIMITER and self.flow_mod_limiter.to_forward_packet()):
            self._of_send(msg)
        
        self.flow_mod_queue.put((current_time, event.parse()))
    def do_pkt_out(self, event=None):

        msg = of.ofp_packet_out()            
        
        # Normal pkt-out
        if event:
            if event.ofp.buffer_id == -1:
                mylog("Not flooding unbuffered packet on", dpidToStr(event.dpid))
                return
            msg.actions.append(of.ofp_action_output(port=get_the_other_port(event.port)))
            msg.buffer_id = event.ofp.buffer_id
            msg.in_port = event.port
            mylog('Normal packet-out: ', msg)
            
        # Special pkt-out that generates a packet that is exactly the same as
        # the trigger packet. Unfortunately, only 114 bytes of the original
        # ingress packet are forwarded into the controller as pkt-in. We need to
        # make up for the truncated length, if needed. The checksum will be
        # wrong, but screw that.
        else:
            raw_data = func_cache(self.trigger_event.parse).raw
            if len(raw_data) < self.pkt_out_length:
                raw_data += 'z' * (self.pkt_out_length - len(raw_data))
            msg._data = raw_data
            msg.buffer_id = -1
            with self.lock:
                assert self.trigger_event
                msg.actions.append(of.ofp_action_output(port=get_the_other_port(self.trigger_event.port)))
                msg.in_port = self.trigger_event.port

            # Stat collection for special pkt-out only.
            current_time = time.time()
            with self.lock:
                (count, start, _) = self.pkt_out_stat
                if start is None: start = current_time
                self.pkt_out_stat = (count + 1, start, current_time)
        
        if (not USE_LIMITER) or (USE_LIMITER and self.dyn_limiter.to_forward_packet(DynamicLimiter.PacketType.PktOut)):
            self._of_send(msg)