Beispiel #1
0
    def split_and_send_data(self, packet, data):
        text_buffer, text_buffer_overflow, text_buffer_len, block = self.src_dest_data_read(
            packet)
        data_len = len(data)

        num_of_full_packets = data_len / utils.MAX_PACKET_SIZE
        size_of_last_packet = data_len - num_of_full_packets * utils.MAX_PACKET_SIZE
        for i in range(0, num_of_full_packets):
            start = i * utils.MAX_PACKET_SIZE
            end = start + utils.MAX_PACKET_SIZE
            payload = data[start:end]
            curr_packet = tcp_packet.Packet(src=packet.src,
                                            dest=packet.dest,
                                            is_raw_data=True,
                                            is_fin=False,
                                            payload=payload)
            self.staff_send(curr_packet)
        last_payload_start = num_of_full_packets * utils.MAX_PACKET_SIZE
        last_payload_end = last_payload_start + size_of_last_packet
        last_payload = data[last_payload_start:last_payload_end]
        last_packet = tcp_packet.Packet(src=packet.src,
                                        dest=packet.dest,
                                        is_raw_data=True,
                                        is_fin=packet.is_fin,
                                        payload=last_payload)
        self.staff_send(last_packet)
    def handle_full_block(self, packet, diff):

        dif = self.BLOCK_SIZE - self.dest_packetSize[packet.dest]
        if dif == packet.size():
            self.dest_packetList[packet.dest].append(
                tcp_packet.Packet(packet.src, packet.dest, True, packet.is_fin,
                                  packet.payload[:dif]))
        else:
            self.dest_packetList[packet.dest].append(
                tcp_packet.Packet(packet.src, packet.dest, True, False,
                                  packet.payload[:dif]))
        self.dest_block[
            packet.dest] = self.dest_block[packet.dest] + packet.payload[:dif]
        hash = utils.get_hash(self.dest_block[packet.dest])
        if hash in self.hash_packetList.keys():
            comp_packet = tcp_packet.Packet(packet.src, packet.dest, False,
                                            packet.is_fin, hash)
            self.send(comp_packet, self.wan_port)
        else:
            self.hash_packetList[hash] = self.dest_packetList[packet.dest]
            self.send_code(packet, self.dest_packetList[packet.dest])
        self.dest_packetSize[packet.dest] = diff

        self.dest_block[packet.dest] = ""
        self.dest_packetList[packet.dest] = []
        if diff != 0:
            self.dest_block[packet.dest] = packet.payload[dif:]
            self.dest_packetList[packet.dest].append(
                tcp_packet.Packet(packet.src, packet.dest, True, packet.is_fin,
                                  packet.payload[dif:]))
Beispiel #3
0
 def send_raw_data(self, pair, block, is_fin, port):
     size = utils.MAX_PACKET_SIZE
     while size < len(block):
         payload = block[:size]
         block = block[size:]
         packet = tcp_packet.Packet(pair[0], pair[1], True, False, payload)
         self.send(packet, port)
     packet = tcp_packet.Packet(pair[0], pair[1], True, is_fin, block)
     self.send(packet, port)
 def give_block(self, packet, message, port, is_fin):
     start_block = 0
     end_block = utils.MAX_PACKET_SIZE
     while end_block < self.BLOCK_SIZE:
         split_msg = message[start_block:end_block]
         self.send(
             tcp_packet.Packet(packet.src, packet.dest, True, False,
                               split_msg), port)
         start_block, end_block = end_block, end_block + utils.MAX_PACKET_SIZE
     carry_over_msg = message[start_block:]
     self.send(
         tcp_packet.Packet(packet.src, packet.dest, True, is_fin,
                           carry_over_msg), port)
 def give_block(self, packet, message, port, is_fin, intent=True):
     # Give block to either Wan or client.
     # Intent being False means to terminate the message on the other Wan end.
     start_block = 0
     end_block = utils.MAX_PACKET_SIZE
     block_message_length = message.__len__()
     while end_block < block_message_length:
         split_msg = message[start_block:end_block]
         self.send(tcp_packet.Packet(packet.src, packet.dest,
                                     True, False, split_msg), port)
         start_block, end_block = end_block, end_block + utils.MAX_PACKET_SIZE
     carry_over_msg = message[start_block:]
     self.send(tcp_packet.Packet(packet.src, packet.dest,
                                 intent, is_fin, carry_over_msg), port)
Beispiel #6
0
 def send_partial_block(self, block, packet_key, is_remote):
     length = len(block)
     packets = []
     for i in range(1500, 9000, 1500):
         if length < i:
             packets.append(tcp_packet.Packet(packet_key[0], packet_key[1], True, True, block[i-1500:length]))
             break
         else:
             packets.append(tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[i-1500:i]))
     if is_remote:
         for packet in packets:
             self.send(packet, self.wan_port)
     else:
         for packet in packets:
             self.send(packet, self.address_to_port[packet_key[1]])
 def send_hash(self, hash_code, packet_key, is_fin, is_remote):
     packet = tcp_packet.Packet(packet_key[0], packet_key[1], False, is_fin,
                                hash_code)
     if is_remote:
         self.send(packet, self.wan_port)
     else:
         self.send(packet, self.address_to_port[packet_key[1]])
Beispiel #8
0
    def send_file(self, input_filename, destination_ip_address):
        """ Sends the given file to the given destination.

        The input_filename is transmitted first, followed by FILENAME_DELIMITER.
        
        Breaks the file up into packets in order to send it.
        """
        f = open(input_filename, 'rb')
        # The filename followed by the delimiter is the first thing sent.
        packet_data = input_filename + FILENAME_DELIMITER
        assert len(packet_data) <= utils.MAX_PACKET_SIZE
        finished_reading = False
        while not finished_reading:
            # Read in a packet worth of data.
            while len(packet_data) < utils.MAX_PACKET_SIZE:
                remaining_data = utils.MAX_PACKET_SIZE - len(packet_data)
                new_data = f.read(remaining_data)
                if not new_data:
                    # There's no data left in the file.
                    finished_reading = True
                    break
                packet_data = packet_data + new_data

            # Send the current packet.
            # Note that this packet may be empty if the file is done reading
            # but we still need to send a FIN packet.
            packet = tcp_packet.Packet(self.ip_address,
                                       destination_ip_address,
                                       is_raw_data=True,
                                       is_fin=finished_reading,
                                       payload=packet_data)
            self.gateway_middlebox.receive(packet)
            packet_data = ""

        f.close()
Beispiel #9
0
 def send_fin(self, destination_ip_address):
     packet = tcp_packet.Packet(self.ip_address,
                                destination_ip_address,
                                is_raw_data=True,
                                is_fin=True,
                                payload="")
     self.gateway_middlebox.receive(packet)
Beispiel #10
0
    def send_code(self, packet, dest, payload, key=None):
        if key and self.hash_payloads.has_key(key):
            self.send(
                tcp_packet.Packet(packet.src, packet.dest, False,
                                  packet.is_fin, key), dest)
        else:
            if key and not self.hash_payloads.has_key(key):
                self.hash_payloads[key] = payload

            while (len(payload) > utils.MAX_PACKET_SIZE):
                p = tcp_packet.Packet(packet.src, packet.dest, True, False,
                                      payload[:utils.MAX_PACKET_SIZE])
                payload = payload[utils.MAX_PACKET_SIZE:]
                self.send(p, dest)
            packet = tcp_packet.Packet(packet.src, packet.dest, True,
                                       packet.is_fin, payload)
            self.send(packet, dest)
 def handle_hash(self, packet, message, hashed_message, is_fin):
     # Check for key...
     if hashed_message not in self.hash_data:
         self.hash_data[hashed_message] = message
         self.give_block(packet, message, self.wan_port, is_fin, False)
     else:
         self.send(tcp_packet.Packet(packet.src, packet.dest,
                                     False, is_fin, hashed_message), self.wan_port)
Beispiel #12
0
    def split_and_send(self, data, packet, wan, fin):
        if wan:
            out = self.wan_port
        else:
            out = self.address_to_port[packet.dest]

        i = 0
        packets = []
        while (i + utils.MAX_PACKET_SIZE) < len(data):
            packets.append(data[i:i + 1500])
            i += 1500

        for p in packets:
            piece = tcp_packet.Packet(packet.src, packet.dest, True, False, p)
            self.send(piece, out)
        final = tcp_packet.Packet(packet.src, packet.dest, True, fin, data[i:])
        self.send(final, out)
 def handle_leftover_packet(self, leftover_packet, leftover_msg):
     # Don't forget to Store hash and send to the other middle box
     buffer_key = (leftover_packet.src, leftover_packet.dest)
     hashed_msg = utils.get_hash(leftover_msg)
     if hashed_msg not in self.hash_data:
         self.hash_data[hashed_msg] = leftover_msg
         self.send(
             tcp_packet.Packet(leftover_packet.src, leftover_packet.dest,
                               True, leftover_packet.is_fin, leftover_msg),
             self.wan_port)
     else:
         # Previous hashed_msg constructed already!
         self.send(
             tcp_packet.Packet(leftover_packet.src, leftover_packet.dest,
                               False, leftover_packet.is_fin, hashed_msg),
             self.wan_port)
     # Remove specific buffer pool information
     self.buffers[buffer_key] = ""
 def send_block(self, block, packet_key, is_fin, is_remote):
     length = len(block)
     i = 0
     packets = []
     while length > 1500:
         packet = tcp_packet.Packet(packet_key[0], packet_key[1], True,
                                    False, block[i:i + 1500])
         packets.append(packet)
         length = length - 1500
         i = i + 1500
     packets.append(
         tcp_packet.Packet(packet_key[0], packet_key[1], True, is_fin,
                           block[i:i + length]))
     for packet in packets:
         if is_remote:
             self.send(packet, self.wan_port)
         else:
             self.send(packet, self.address_to_port[packet_key[1]])
Beispiel #15
0
    def process(self, packet, to_wan):
        start = max(len(self.buffer[packet.src][packet.dest]) - 48, 0)
        end = start + 48
        self.buffer[packet.src][packet.dest] += packet.payload
        curr_window = self.buffer[packet.src][packet.dest][start:end]

        while end <= len(self.buffer[packet.src][packet.dest]):
            curr_hash = utils.get_hash(curr_window)
            if utils.get_last_n_bits(
                    curr_hash, 13) == WanOptimizer.GLOBAL_MATCH_BITSTRING:
                data = self.buffer[packet.src][packet.dest][:end]
                my_hash = utils.get_hash(data)

                if my_hash in self.storage and to_wan:
                    self.send(
                        tcp_packet.Packet(packet.src, packet.dest, False,
                                          False, my_hash), self.wan_port)
                else:
                    self.storage[my_hash] = data
                    self.split_and_send(data, packet, to_wan, False)

                self.buffer[packet.src][packet.dest] = self.buffer[packet.src][
                    packet.dest][end:]
                start = 0
                end = 48
            else:
                start += 1
                end += 1

            curr_window = self.buffer[packet.src][packet.dest][start:end]

        if packet.is_fin:
            data = self.buffer[packet.src][packet.dest]
            my_hash = utils.get_hash(data)
            if my_hash in self.storage and to_wan:
                self.send(
                    tcp_packet.Packet(packet.src, packet.dest, False, True,
                                      my_hash), self.wan_port)
            else:
                self.storage[my_hash] = data
                self.split_and_send(data, packet, to_wan, True)

            self.buffer[packet.src][packet.dest] = ''
 def send_code(self, packet, packet_lst):
     # Send the packets in the block
     for p in packet_lst:
         comp_packet = tcp_packet.Packet(packet.src, packet.dest,
                                         p.is_raw_data, p.is_fin, p.payload)
         if packet.dest in self.address_to_port:
             # Sending to the clients connected to this middlebox
             self.send(comp_packet, self.address_to_port[comp_packet.dest])
         else:
             # sending thru the WAN.
             self.send(comp_packet, self.wan_port)
 def handle_partial_block(self, packet):
     self.dest_packetSize[packet.dest] += packet.size()
     self.dest_block[packet.dest] += packet.payload
     self.dest_packetList[packet.dest].append(packet)
     if packet.is_fin:
         # lets send what we have so far
         hash = utils.get_hash(self.dest_block[packet.dest])
         if hash in self.hash_packetList:
             comp_packet = tcp_packet.Packet(packet.src, packet.dest, False,
                                             False, hash)
             self.send(comp_packet, self.wan_port)
         else:
             self.hash_packetList[hash] = self.dest_packetList[packet.dest]
             self.send_code(packet, self.dest_packetList[packet.dest])
         self.initializer(packet, True)
 def send_all_wan(self, packet, is_fin):
     # Don't forget to Store hash and send to the other middle box
     # send_all_wan should only hash in self.BLOCK_SIZE
     buffer_key = (packet.src, packet.dest)
     message = self.buffers[buffer_key]
     hashed_msg = utils.get_hash(message)
     if hashed_msg not in self.hash_data:
         self.hash_data[hashed_msg] = message
         self.give_block(packet, message, self.wan_port, is_fin)
     else:
         self.send(
             tcp_packet.Packet(packet.src, packet.dest, False, is_fin,
                               hashed_msg), self.wan_port)
     # Remove specific buffer pool information
     self.buffers[buffer_key] = ""
Beispiel #19
0
    def send_data(self, data_to_send, destination_ip_address):
        """ Packetizes and sends the given data.

        This method does not send a FIN packet, so can be used repeatedly to send
        data to the same destination before ending the stream.
        """
        start = 0
        while start < len(data_to_send):
            end = start + utils.MAX_PACKET_SIZE
            packet = tcp_packet.Packet(self.ip_address,
                                       destination_ip_address,
                                       is_raw_data=True,
                                       is_fin=False,
                                       payload=data_to_send[start:end])
            self.gateway_middlebox.receive(packet)
            start = start + utils.MAX_PACKET_SIZE
Beispiel #20
0
 def send_data_with_fin(self, data_to_send, destination_ip_address):
     """ Packetizes and sends the given data. Last packet is a fin
     """
     start = 0
     packets_to_send = []
     while start < len(data_to_send):
         end = start + utils.MAX_PACKET_SIZE
         packet = tcp_packet.Packet(self.ip_address,
                                    destination_ip_address,
                                    is_raw_data=True,
                                    is_fin=False,
                                    payload=data_to_send[start:end])
         packets_to_send.append(packet)
         start = start + utils.MAX_PACKET_SIZE
     last_packet = packets_to_send[-1]
     last_packet.is_fin = True
     packets_to_send[-1] = last_packet
     for p in packets_to_send:
         self.gateway_middlebox.receive(packet)
Beispiel #21
0
 def send_full_block(self, block, packet_key, is_fin, is_remote):
     packets = [tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[:1500]),
         tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[1500:3000]),
         tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[3000:4500]),
         tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[4500:6000]),
         tcp_packet.Packet(packet_key[0], packet_key[1], True, False, block[6000:7500]),
         tcp_packet.Packet(packet_key[0], packet_key[1], True, is_fin, block[7500:])
     ]
     if is_remote:
         for packet in packets:
             self.send(packet, self.wan_port)
     else:
         for packet in packets:
             self.send(packet, self.address_to_port[packet_key[1]])
Beispiel #22
0
    def send_single(self, packet, payload, is_fin):
        """ A helper function to send just a single packet to its "appropriate" destination.
            The difference between this function and the base `wan_optimizer.send()` function is that:
            The base function sends the input `packet` to a `dest`.
            This function CREATES a packet using the input params and determines its destination based on `packet.dest` and then sends it.

            PARAMS:
            packet: We will extract packet.src and packet.dest from this param.
            block: Raw payload that should fit in the SINGLE packet that will be created and sent.
            is_fin: Whether this new packet should be a `fin` packet.
        """

        # Construct a packet from input params. Assumes that `is_raw_data` is True.
        pkt = tcp_packet.Packet(packet.src, packet.dest, True, is_fin, payload)

        # This is a OPT(B) so we send to a client.
        if self.is_receiver(packet):
            self.send(pkt, self.address_to_port[packet.dest])

        # This is a OPT(A) so we send to the WAN.
        else:
            self.send(pkt, self.wan_port)
Beispiel #23
0
    def receive(self, packet):
        """ Handles receiving a packet.

        We define two types of OPT:
        OPT(A): The OPT on the sending side. This type of OPT can ONLY receive raw data but can send both raw data or hashes.
        OPT(B): The OPT on the receiving side. This type of OPT can receive both raw data and hashes but can ONLY send raw data.
        ** This is evidence that a OPT is a middle box, i.e. clients receive and send ONLY raw data.

        A WAN optimizer can receive packets in 2 forms:
        1. The payload is raw data  ->  The packet is from a client to an OPT(A)/OPT(B) OR 
                                        The packet is from the WAN to an OPT(B) and this OPT(B) hasn't cached it yet.
        2. The payload is a hash    ->  The packet is from the WAN to an OPT(B) and this OPT(B) has already cached it.

        Processing:
        -> If we receive a hash (we must be an OPT(B)), we assume the block is in our cache so we get the corresponding block from the cache 
           and then send it via MULTIPLE packets using `send_block()`.
        -> If we receive raw data, then we want to keep sliding until we find a matching bitstring so that we have a defined block. Once we slide 
           until the end of the buffer and still no matching bitstring is found, two things can happen:
           1. The packet is a `fin` packet, so we must flush everything. Therefore, we treat the whole remaining block as a defined block.
           2. The packet is not a `fin` packet, so we just save the current state of the buffer and wait for the next packet to be received.

           Once we have the a defined block, we hash the block and two things can happen:
           1. The corr. hash is not in the cache that means its the first time seeing it. (OPT(A) and OPT(B))
              So we cache the hash along with the raw data and send the block of raw data to the appropriate next hop via MULTIPLE packets using `send_block()`.
           2. The corr. hash is in the cache that means we have seen this block before. (Only OPT(A) will reach this case)
              Therefore we send the hash in ONE packet to the next hop WAN using `send_single()`.
              (!!) An OPT(B) should NEVER EVER reach this case since if it did indeed cache this block before, a hash should be sent to it instead of raw data.

        """
        # Case when the packet received has raw data.
        if packet.is_raw_data:

            # Initialize a buffer for this (packet.src, packet.dest) pairing if none exist yet.
            if (packet.src, packet.dest) not in self.buffers:
                self.set_buf(packet, "")
                self.reset_ptr(packet)

            # `cur_buf` holds the current buffer including the incoming packet.
            cur_buf = self.get_buf(packet) + packet.payload
            cur_buf_len = len(cur_buf)

            # In this case, we do not expect more packets to come. So we need to flush everything out of the buffer.
            if packet.is_fin:

                # The buffer is less than `WINDOW_SIZE` which means we cannot do bitstring matching and since we need to flush, we just cache it and send it along.
                if cur_buf_len < self.WINDOW_SIZE:
                    hashed_value = utils.get_hash(cur_buf)

                    # Cache if uncached and send along.
                    if hashed_value not in self.cache:
                        self.cache[hashed_value] = cur_buf
                        self.send_single(packet, cur_buf, True)
                    # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash instead.
                    else:
                        pkt = tcp_packet.Packet(packet.src, packet.dest, False,
                                                True, hashed_value)
                        self.send(pkt, self.wan_port)

                    # Reset buffer and end pointer
                    self.reset_buf(packet)
                    self.reset_ptr(packet)

                # Case where we have equal or more than 48 bytes in the buffer, so we keep sliding and flush everything.
                else:
                    # Keep looping until we flush everything
                    while (True):
                        end = self.get_ptr(packet)
                        start = end - self.WINDOW_SIZE
                        window = cur_buf[start:end]

                        # ----------------------------------------------------------------------------------------------------------------------
                        # The `window` length is smaller than `WINDOW_SIZE` when we slide past the buffer. This means we should cache the whole buffer
                        # and send tho whole buffer via MULTIPLE packets.
                        if len(window) < self.WINDOW_SIZE:
                            hashed_value = utils.get_hash(cur_buf)

                            # No matter if this is OPT(A) or OPT(B), if uncached, we cache it and send the whole buffer via MULTIPLE packets.
                            if hashed_value not in self.cache:
                                self.cache[hashed_value] = cur_buf
                                # The last packet shld have `is_fin` set to True.
                                self.send_block(packet, cur_buf, True)

                            # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                            else:
                                # Construct packet w/ payload = hashed_value and send it out to the next WAN.
                                pkt = tcp_packet.Packet(
                                    packet.src, packet.dest, False, True,
                                    hashed_value)
                                self.send(pkt, self.wan_port)

                            # Reset buffer and end pointer
                            self.reset_buf(packet)
                            self.reset_ptr(packet)
                            break
                        # ----------------------------------------------------------------------------------------------------------------------

                        hashed_window = utils.get_hash(window)
                        bitstring = utils.get_last_n_bits(
                            hashed_window, self.BITSTRING_SIZE)

                        # We have a matching bitstring so we now have a defined block
                        if (bitstring == self.GLOBAL_MATCH_BITSTRING):
                            # The defined block should be from the very front of the buffer up till the end of the window.
                            defined_block = cur_buf[:end]
                            hashed_value = utils.get_hash(defined_block)

                            # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via MULTIPLE packets.
                            if hashed_value not in self.cache:
                                self.cache[hashed_value] = defined_block
                                # This is not the last packet so we pass in `is_fin` as False.
                                self.send_block(packet, defined_block, False)

                            # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                            else:
                                # Construct packet w/ payload = hashed_value and send it out to the next WAN.
                                pkt = tcp_packet.Packet(
                                    packet.src, packet.dest, False, False,
                                    hashed_value)
                                self.send(pkt, self.wan_port)

                            # Now we need to remove the processed portion of the buffer and set the appropriate length.
                            cur_buf = cur_buf[end:]
                            cur_buf_len = len(cur_buf)

                            # Since the buffer is partially processed, we need to reset the end pointer.
                            self.reset_ptr(packet)

                        # We did not get a matching bitstring, so we slide by a byte, i.e. we increment the end pointer, and then reloop.
                        else:
                            self.set_ptr(packet, end + 1)

            # In this case, `is_fin` is False which means we expect more packets, so we just process however much of the buffer we can.
            else:
                # The buffer is less than `WINDOW_SIZE` which means we cannot do bitstring matching and since we DON'T need to flush,
                # we just update the buffer in our cache and wait for the next packet to arrive.
                if cur_buf_len < self.WINDOW_SIZE:
                    self.set_buf(packet, cur_buf)

                # Case where we have equal or more than 48 bytes in the buffer, so we keep sliding and trying to find and process defined blocks.
                # If there is anything remaining in the buffer, just save that buffer and record the pointer to where we left off.
                else:
                    # Keep looping and processing.
                    while (True):
                        end = self.get_ptr(packet)
                        start = end - self.WINDOW_SIZE
                        window = cur_buf[start:end]

                        # ----------------------------------------------------------------------------------------------------------------------
                        # The `window` length is smaller than `WINDOW_SIZE` when we slide past the buffer. This means we should just save the whole buffer.
                        # Since the end pointer was not modified, we don't need to do anything to it.
                        if len(window) < self.WINDOW_SIZE:
                            self.set_buf(packet, cur_buf)
                            break
                        # ----------------------------------------------------------------------------------------------------------------------

                        hashed_window = utils.get_hash(window)
                        bitstring = utils.get_last_n_bits(
                            hashed_window, self.BITSTRING_SIZE)

                        # We have a matching bitstring so we now have a defined block
                        if (bitstring == self.GLOBAL_MATCH_BITSTRING):
                            # The defined block should be from the very front of the buffer till the end of the window.
                            defined_block = cur_buf[:end]
                            hashed_value = utils.get_hash(defined_block)

                            # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via MULTIPLE packets.
                            if hashed_value not in self.cache:
                                self.cache[hashed_value] = defined_block
                                # This is not the last packet so we pass in `is_fin` as False.
                                self.send_block(packet, defined_block, False)

                            # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                            else:
                                # Construct packet w/ payload = hashed_value and send to the next WAN.
                                pkt = tcp_packet.Packet(
                                    packet.src, packet.dest, False, False,
                                    hashed_value)
                                self.send(pkt, self.wan_port)

                            # Now we need to remove the processed portion of the buffer
                            cur_buf = cur_buf[end:]
                            cur_buf_len = len(cur_buf)

                            # Since the buffer is partially processed, we need to reset the end pointer.
                            self.reset_ptr(packet)

                        # We did not get a matching bitstring, so we slide by a byte, i.e. we increment the end pointer, and then reloop.
                        else:
                            self.set_ptr(packet, end + 1)

        # Case when the packet received is a hash (this MUST be an OPT(B)).
        # When a hash was received, it means that the block should be in the cache.
        else:
            # Obtain the cached block from cache and send it via `send_block()`.
            defined_block = self.cache[packet.payload]
            # The last packet sent should inherit `packet.is_fin`.
            self.send_block(packet, defined_block, packet.is_fin)
Beispiel #24
0
    def receive(self, packet):
        """ Handles receiving a packet.

        We define two types of OPT:
        OPT(A): The OPT on the sending side. This type of OPT can ONLY receive raw data but can send both raw data or hashes.
        OPT(B): The OPT on the receiving side. This type of OPT can receive both raw data and hashes but can ONLY send raw data.
        ** This is evidence that a OPT is a middle box, i.e. clients receive and send ONLY raw data.

        A WAN optimizer can receive packets in 2 forms:
        1. The payload is raw data  ->  The packet is from a client to an OPT(A)/OPT(B) OR 
                                        The packet is from the WAN to an OPT(B) and this OPT(B) hasn't cached it yet.
        2. The payload is a hash    ->  The packet is from the WAN to an OPT(B) and this OPT(B) has already cached it.

        Processing:
        -> If we receive a hash (we must be an OPT(B)), we get the corresponding block from the cache and then send it via MULTIPLE packets using `send_block()`.
        -> If we receive raw data, then we need to accumulate `BLOCK_SIZE` worth of data before we can process anything.
           Once we have the `BLOCK` accumulated, we hash the block and two things can happen:
           1. The corr. hash is not in the cache that means its the first time seeing it. (OPT(A) and OPT(B))
              So we cache the hash along with the raw data and send the block of raw data to the appropriate next hop via MULTIPLE packets using `send_block()`.
           2. The corr. hash is in the cache that means we have seen this `BLOCK` before. (Only OPT(A) will reach this case)
              Therefore we send the hash in ONE packet to the next hop WAN using `send_single()`.
              (!!) An OPT(B) should NEVER EVER reach this case since if it did indeed cache this `BLOCK` before, a hash should be sent to it instead of raw data.

        """

        # Case when the packet received has raw data.
        if packet.is_raw_data:

            # Initialize a buffer for this (packet.src, packet.dest) pairing if none exist yet.
            if (packet.src, packet.dest) not in self.buffers:
                self.set_buf(packet, "")

            cur_buf_len = len(self.get_buf(packet))
            cur_pkt_len = len(packet.payload)

            # Decide course of action based on how much is the buffer filled.
            # Case where we have buffer be exactly full, i.e. no overflow.
            if (cur_buf_len + cur_pkt_len == self.BLOCK_SIZE):

                raw_data_block = self.get_buf(packet) + packet.payload
                hashed_value = utils.get_hash(raw_data_block)

                # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via MULTIPLE packets.
                if hashed_value not in self.cache:
                    self.cache[hashed_value] = raw_data_block
                    # `packet.is_fin` should be passed in due to inheritance.
                    self.send_block(packet, raw_data_block, packet.is_fin)

                # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                else:
                    # Construct packet w/ payload = hashed_value
                    # `packet.is_fin` should be passed in due to inheritance.
                    pkt = tcp_packet.Packet(packet.src, packet.dest, False,
                                            packet.is_fin, hashed_value)
                    self.send(pkt, self.wan_port)

                # We have finished processing the accumulated buffer, so we reset the buffer.
                self.reset_buf(packet)

            # Case where we have buffer overflow so we process one block and store the excess overflowed portion in the buffer.
            # There is an edge case where if this overflowing packet is a `fin` packet, then we would have to send it too.
            elif (cur_buf_len + cur_pkt_len > self.BLOCK_SIZE):

                # Split out the overflowed portion
                portion_main = packet.payload[0:(self.BLOCK_SIZE -
                                                 cur_buf_len)]
                portion_overflow = packet.payload[(self.BLOCK_SIZE -
                                                   cur_buf_len):]
                raw_data_block = self.get_buf(packet) + portion_main
                hashed_value = utils.get_hash(raw_data_block)

                # Deal with main portion (the full block) first
                # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via MULTIPLE packets.
                if hashed_value not in self.cache:
                    self.cache[hashed_value] = raw_data_block
                    # Since this is the overflow case, the `is_fin` flag we pass in must be False.
                    self.send_block(packet, raw_data_block, False)

                # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                else:
                    # Construct packet w/ payload = hashed_value
                    # Since this is the overflow case, the `is_fin` flag we pass in must be False.
                    pkt = tcp_packet.Packet(packet.src, packet.dest, False,
                                            False, hashed_value)
                    self.send(pkt, self.wan_port)

                # We have finished processing the accumulated buffer, so we reset the buffer.
                self.reset_buf(packet)

                # Now deal with overflowed portion. (!!) Overflowed portion MUST fit in a single packet.
                # Immediately deal with overflowed portions from `fin` packets.
                if packet.is_fin:
                    overflow_hashed_value = utils.get_hash(portion_overflow)

                    # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via a SINGLE packet to appropriate destination.
                    if overflow_hashed_value not in self.cache:
                        self.cache[overflow_hashed_value] = portion_overflow
                        # The `is_fin` flag we pass in must be True.
                        self.send_single(packet, portion_overflow, True)

                    # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                    else:
                        # Construct packet w/ payload = overflow_hashed_value
                        # The `is_fin` flag we pass in must be True.
                        pkt_to_send = tcp_packet.Packet(
                            packet.src, packet.dest, False, True,
                            overflow_hashed_value)
                        self.send(pkt_to_send, self.wan_port)

                # If overflowed packet is not `fin` packet, then just store the overflowed portion in the buffer
                else:
                    self.set_buf(packet, portion_overflow)

            # Case where we don't have a full buffer. So, we only need to process if the packet's `is_fin` is set to True.
            else:
                if packet.is_fin:
                    raw_data_block = self.get_buf(packet) + packet.payload
                    hashed_value = utils.get_hash(raw_data_block)

                    # No matter if this is OPT(A) or OPT(B), if uncached, we cache the hash and send the whole block via MULTIPLE packets.
                    if hashed_value not in self.cache:
                        self.cache[hashed_value] = raw_data_block
                        # The `is_fin` flag we pass in must be True.
                        self.send_block(packet, raw_data_block, True)

                    # Since we have raw data input and it is cached, we must be OPT(A), so we send the hash to WAN via a SINGLE packet.
                    else:
                        # Construct packet w/ payload = hashed_value.
                        # The `is_fin` flag we pass in must be True.
                        pkt_to_send = tcp_packet.Packet(
                            packet.src, packet.dest, False, True, hashed_value)
                        self.send(pkt_to_send, self.wan_port)

                    # We have finished processing the "even though not full but has a `fin` packet" buffer, so we reset the buffer.
                    self.reset_buf(packet)

                # Just put stuff in the buffer
                else:
                    self.set_buf(packet, self.get_buf(packet) + packet.payload)

        # Case when the packet received is a hash (this MUST be an OPT(B)).
        # When a hash was received, it means that the block should be in the cache.
        else:
            # Obtain the cached block from cache and send it via `send_block()`.
            raw_data_block = self.cache[packet.payload]
            self.send_block(packet, raw_data_block, packet.is_fin)
Beispiel #25
0
    def receive(self, packet):
        """ Handles receiving a packet.

        Right now, this function simply forwards packets to clients (if a packet
        is destined to one of the directly connected clients), or otherwise sends
        packets across the WAN. You should change this function to implement the
        functionality described in part 1.  You are welcome to implement private
        helper fuctions that you call here. You should *not* be calling any functions
        or directly accessing any variables in the other middlebox on the other side of 
        the WAN; this WAN optimizer should operate based only on its own local state
        and packets that have been received.
        """

        if (packet.is_raw_data == True):
            text_buffer, text_buffer_overflow, text_buffer_len, block = self.src_dest_data_read(
                packet)

            # block.append(packet);

            self.fill_buffer(packet)
            text_buffer, text_buffer_overflow, text_buffer_len, block = self.src_dest_data_read(
                packet)

            buffer_flushed = False
            if (text_buffer_len == WanOptimizer.BLOCK_SIZE):
                hashcode = utils.get_hash(text_buffer)

                # ERIC: Set curr_is_fin for the packet_with_hash
                curr_is_fin = packet.is_fin

                if (len(text_buffer_overflow) > 0):
                    curr_is_fin = False

                if (hashcode in self.hash_to_data.keys()):
                    packet_with_hash = tcp_packet.Packet(src=packet.src,
                                                         dest=packet.dest,
                                                         is_raw_data=False,
                                                         is_fin=curr_is_fin,
                                                         payload=hashcode)
                    self.staff_send(packet_with_hash)
                else:
                    self.hash_to_data[hashcode] = text_buffer
                    self.flush_buffer(packet)
                    # self.hash_block(packet);
                buffer_flushed = True

            # ERIC: Reset the buffers here but also check if packet.is_fin.
            # If the packet.is_fin, then we check to see if there's unsent data.
            # If there is unsent data, then we hash the remaining data and send it out.
            if (packet.is_fin == True):
                if (buffer_flushed == True):
                    self.reset_buffers(packet)
                    text_buffer, text_buffer_overflow, text_buffer_len, block = self.src_dest_data_read(
                        packet)

                # if (text_buffer_len > 0):
                # hashcode = self.hash_block;
                # print hashcode
                hashcode = utils.get_hash(text_buffer)
                #AFTER WE DISCOVERED ABOVE DOES NOTHING
                if (hashcode in self.hash_to_data.keys()):
                    if packet.dest in self.address_to_port:
                        raw_data = self.hash_to_data[hashcode]
                        self.split_and_send_data(packet, raw_data)
                    else:
                        packet.payload = hashcode
                        self.staff_send(packet)
                else:
                    if packet.payload:  # If payload is not empty
                        self.hash_to_data[hashcode] = text_buffer
                    self.flush_buffer(packet)
                self.reset_buffers(packet)
            else:
                if (buffer_flushed == True):
                    self.reset_buffers(packet)

        else:  # Eric: packet contains a hashcode
            received_hash = packet.payload
            text_buffer, text_buffer_overflow, text_buffer_len, block = self.src_dest_data_read(
                packet)
            # Check if its in the dict. If it isn't, INVALID HASH
            if received_hash in self.hash_to_data.keys():
                if packet.dest in self.address_to_port:  #ERIC: If we are sending to a client, then we don't send the hash
                    # The packet is destined to one of the clients connected to this middlebox;
                    # send the packet there.
                    if (text_buffer == True):
                        self.hash_to_data[hashcode] = text_buffer
                        flush_buffer(packet)
                        self.reset_buffers(packet)
                    raw_data = self.hash_to_data[received_hash]
                    self.split_and_send_data(packet, raw_data)
                else:
                    # The packet must be destined to a host connected to the other middlebox
                    # so send it across the WAN.
                    if (text_buffer == True):
                        self.hash_to_data[hashcode] = text_buffer
                        flush_buffer(packet)
                        self.reset_buffers(packet)
                    self.staff_send(packet)
Beispiel #26
0
 def send_hash(self, pair, hash_value, is_fin):
     packet = tcp_packet.Packet(pair[0], pair[1], False, is_fin, hash_value)
     self.send(packet, self.wan_port)