def send_multiple(block_payload, port, is_fin): while len(block_payload) > utils.MAX_PACKET_SIZE: pckt = Packet(packet_key[0], packet_key[1], True, False, block_payload[:utils.MAX_PACKET_SIZE]) block_payload = block_payload[utils.MAX_PACKET_SIZE:] self.send(pckt, port) pckt = Packet(packet_key[0], packet_key[1], True, is_fin, block_payload) self.send(pckt, port)
def send_multiple(block_payload, port, is_fin): packets = [] for i in range(0, len(block_payload), utils.MAX_PACKET_SIZE): pload = block_payload[i:i + utils.MAX_PACKET_SIZE] pckt = Packet(packet_key[0], packet_key[1], True, False, pload) self.send(pckt, port) if is_fin: pckt = Packet(packet_key[0], packet_key[1], True, True, '') self.send(pckt, port)
def send_payload_by_splitting(self, payload, source, destination, is_fin): packets = [] while len(payload) > utils.MAX_PACKET_SIZE: packets.append( Packet(source, destination, True, False, payload[:utils.MAX_PACKET_SIZE])) payload = payload[utils.MAX_PACKET_SIZE:] packets.append(Packet(source, destination, True, is_fin, payload)) for pack in packets: self.send(pack, self.address_to_port[destination])
def packetize_send(self, flow, is_fin, data, port): source = flow[0] destination = flow[1] total_packets = len(data) / utils.MAX_PACKET_SIZE for x in range(total_packets): packet = data[x * utils.MAX_PACKET_SIZE : (x + 1) * utils.MAX_PACKET_SIZE] self.send(Packet(source, destination, True, False, packet), port) leftovers = total_packets * utils.MAX_PACKET_SIZE last_packet = Packet(source, destination, True, is_fin, data[leftovers:]) self.send(last_packet, port)
def send_data_fin(self, data, src, dest): while len(data) > utils.MAX_PACKET_SIZE: if dest in self.address_to_port: self.send(Packet(src, dest, True, False, data[:utils.MAX_PACKET_SIZE]), self.address_to_port[dest]) else: self.send(Packet(src, dest, True, False, data[:utils.MAX_PACKET_SIZE]), self.wan_port) data = data[utils.MAX_PACKET_SIZE:] if dest in self.address_to_port: self.send(Packet(src, dest, True, True, data), self.address_to_port[dest]) else: self.send(Packet(src, dest, True, True, data), self.wan_port)
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: packet_to_send = packet packet_for_later = None if (packet.src, packet.dest) not in self.buffers.keys(): self.buffers[(packet.src, packet.dest)] = {} self.buffers[(packet.src, packet.dest)]["packets"] = [] self.buffers[(packet.src, packet.dest)]["length"] = 0 if packet.size() + self.buffers[ (packet.src, packet.dest)]["length"] > WanOptimizer.BLOCK_SIZE: diff = WanOptimizer.BLOCK_SIZE - self.buffers[ (packet.src, packet.dest)]["length"] packet_to_send = Packet(packet.src, packet.dest, packet.is_raw_data, False, packet.payload[:diff]) packet_for_later = Packet(packet.src, packet.dest, packet.is_raw_data, packet.is_fin, packet.payload[diff:]) self.buffers[(packet.src, packet.dest)]['packets'].append(packet_to_send) self.buffers[(packet.src, packet.dest)]['length'] += packet.size() if packet.is_fin or self.buffers[ (packet.src, packet.dest)]['length'] >= WanOptimizer.BLOCK_SIZE: self.send_block( self.buffers[(packet.src, packet.dest)]["packets"], packet.src, packet.dest, packet.is_fin) self.buffers[(packet.src, packet.dest)]['packets'] = [] self.buffers[(packet.src, packet.dest)]['length'] = 0 if packet_for_later: self.buffers[(packet.src, packet.dest)]['packets'] = [ packet_for_later ] self.buffers[( packet.src, packet.dest)]['length'] = packet_for_later.size() else: if packet.dest in self.address_to_port: if packet.payload in self.hash_to_data.keys(): raw_data = self.hash_to_data[packet.payload] self.send_payload_by_splitting(raw_data, packet.src, packet.dest, packet.is_fin)
def packetize_and_send(self, to_send, flow, is_fin, outgoing_port): """Packetizes and sends everything in to_send""" src, dest = flow[0], flow[1] num_full_packets = len(to_send) / utils.MAX_PACKET_SIZE for i in range(num_full_packets): subset = to_send[i * utils.MAX_PACKET_SIZE:(i + 1) * utils.MAX_PACKET_SIZE] new_packet = Packet(src, dest, True, False, subset) self.send(new_packet, outgoing_port) # leftovers - note will send an empty packet on purpose if nothing left subset = to_send[num_full_packets * utils.MAX_PACKET_SIZE:] tail_packet = Packet(src, dest, True, is_fin, subset) self.send(tail_packet, outgoing_port)
def send_block(self, block, src, dest, is_fin=False): #DONT FORGET TO COMPUTE THE HASH AND STORE IT full_data_string = "".join(block) self.hash_to_data[utils.get_hash(full_data_string)] = block for i, payload in enumerate(block): packet = Packet(src, dest, True, is_fin and i == len(block) - 1, payload) if dest in self.address_to_port: self.send(packet, self.address_to_port[packet.dest]) else: self.send(packet, self.wan_port)
def send_remaining_in_buffer(self, flow): # Hash and sends whatever is left in the buffer curr_buffer = self.flows_to_buffers[flow] hashed = utils.get_hash(curr_buffer) src, dest = flow[0], flow[1] # cache = self.flows_to_caches[flow] cache = self.caches if hashed in cache: # send hashed hash_packet = Packet(src, dest, False, True, hashed) # is_fin = True self.send(hash_packet, self.wan_port) else: # hash and send raw data if (len(curr_buffer) > 0): cache[hashed] = curr_buffer self.packetize_and_send(curr_buffer, flow, True, self.wan_port)
def split_and_send_data(self, packet, data): 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 = Packet(src=packet.src, dest=packet.dest, is_raw_data=True, is_fin=False, payload=payload); self.send_packet(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 = Packet(src=packet.src, dest=packet.dest, is_raw_data=True, is_fin=False, payload=last_payload); self.send_packet(last_packet);
def flush_buffer_source(self, flow): curr_buffer = self.flows_to_buffers[flow] hashed = utils.get_hash(curr_buffer) cache = self.caches # Can assume not handling fin packet if (hashed is not None) and (hashed in cache): # send hashed block src, dest = flow hash_packet = Packet(src, dest, False, False, hashed) self.send(hash_packet, self.wan_port) else: # hash and send data cache = self.caches curr_buffer = self.flows_to_buffers[flow] if hashed is not None: cache[hashed] = curr_buffer self.packetize_and_send(curr_buffer, flow, False, self.wan_port) self.reset_buffer(flow)
def send_block(self, list_of_packets, source, destination, is_fin): complete_block = "" for packet in list_of_packets: complete_block += packet.payload hashed_data = utils.get_hash(complete_block) if hashed_data in self.hash_to_data.keys(): if packet.dest in self.address_to_port: for packet in list_of_packets: self.send(packet, self.address_to_port[packet.dest]) else: hashed_packet = Packet(src=source, dest=destination, is_raw_data=False, is_fin=is_fin, payload=hashed_data) self.send(hashed_packet, self.wan_port) else: self.hash_to_data[hashed_data] = complete_block for packet in list_of_packets: self.send_packet(packet)
def handle_flow_fin_source(self, flow): # Hashes and sends whatever is left in the buffer # and does flow cleanup curr_buffer = self.flows_to_buffers[flow] hashed = utils.get_hash(curr_buffer) cache = self.caches if (hashed is not None) and (hashed in cache): src, dest = flow[0], flow[1] hash_packet = Packet(src, dest, False, True, hashed) # is_fin = True self.send(hash_packet, self.wan_port) else: curr_buffer = self.flows_to_buffers[flow] if hashed is not None: cache[hashed] = curr_buffer self.packetize_and_send(curr_buffer, flow, True, self.wan_port) self.close_flow(flow)
def buffer_cache_and_send(self, packet): flow = (packet.src, packet.dest) curr_buffer = self.flows_to_buffers[flow] buffer_size = len(curr_buffer) remaining_bytes = self.BLOCK_SIZE - buffer_size if packet.size() < remaining_bytes: self.flows_to_buffers[flow] = curr_buffer + packet.payload else: # buffer full to_hash = curr_buffer + packet.payload[:remaining_bytes] hashed = utils.get_hash(to_hash) # cache = self.flows_to_caches[flow] cache = self.caches # All FIN packets sent by send_remaining_in_buffer if hashed in cache: # send hashed block hash_packet = Packet(packet.src, packet.dest, False, False, hashed) self.send(hash_packet, self.wan_port) else: # hash and send data cache[hashed] = to_hash self.packetize_and_send(to_hash, flow, False, self.wan_port) self.flows_to_buffers[flow] = packet.payload[remaining_bytes:]
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 2. 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. """ flow = (packet.src, packet.dest) if not flow in self.flow_to_buffer: self.flow_to_buffer[flow] = "" WINDOW_SIZE = 48 if packet.dest in self.address_to_port: # The packet is destined to one of the clients connected to this middlebox; # send the packet there. if packet.is_raw_data: self.send(packet, self.address_to_port[packet.dest]) self.flow_to_buffer[flow] += packet.payload pointer = WINDOW_SIZE buffer_size = len(self.flow_to_buffer[flow]) while pointer <= buffer_size: data_hash = utils.get_hash( self.flow_to_buffer[flow][pointer - WINDOW_SIZE:pointer]) if (utils.get_last_n_bits(data_hash, len(self.GLOBAL_MATCH_BITSTRING)) == self.GLOBAL_MATCH_BITSTRING): current_buff = self.flow_to_buffer[flow][:pointer] self.cache[utils.get_hash(current_buff)] = current_buff # self.packetize_send(cache[hashed], flow, packet.is_fin, self.address_to_port[packet.dest]) self.flow_to_buffer[flow] = self.flow_to_buffer[flow][ pointer:] pointer = WINDOW_SIZE else: pointer += 1 else: self.packetize_send(flow, packet.is_fin, self.cache[packet.payload], self.address_to_port[packet.dest]) if packet.is_fin: data = utils.get_hash(self.flow_to_buffer[flow]) if data is not None: current_buff = self.flow_to_buffer[flow] self.cache[data] = current_buff del self.flow_to_buffer[flow] else: # The packet must be destined to a host connected to the other middlebox # so send it across the WAN. self.flow_to_buffer[flow] += packet.payload pointer = WINDOW_SIZE buffer_size = len(self.flow_to_buffer[flow]) while pointer <= buffer_size: data_hash = utils.get_hash( self.flow_to_buffer[flow][pointer - WINDOW_SIZE:pointer]) if (utils.get_last_n_bits(data_hash, len(self.GLOBAL_MATCH_BITSTRING)) == self.GLOBAL_MATCH_BITSTRING): block = utils.get_hash(self.flow_to_buffer[flow][:pointer]) if block not in self.cache: current_buff = self.flow_to_buffer[flow][:pointer] self.cache[block] = current_buff self.packetize_send(flow, False, current_buff, self.wan_port) else: src, dest = flow self.send(Packet(src, dest, False, False, block), self.wan_port) self.flow_to_buffer[flow] = self.flow_to_buffer[flow][ pointer:] pointer = WINDOW_SIZE else: pointer += 1 if packet.is_fin: flow = (packet.src, packet.dest) current_buff = self.flow_to_buffer[flow] data_hash = utils.get_hash(current_buff) if data_hash not in self.cache: current_buff = self.flow_to_buffer[flow] if data_hash is not None: self.cache[data_hash] = current_buff self.packetize_send(flow, True, current_buff, self.wan_port) else: src, dest = flow[0], flow[1] hash_packet = Packet(src, dest, False, True, data_hash) # set is_fin self.send(hash_packet, self.wan_port) del self.flow_to_buffer[flow]
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 not packet.is_raw_data: if packet.dest in self.address_to_port: # Can't we assume that we have the hash? block = self.hash_to_data[packet.payload] packets = [] while len(block) > utils.MAX_PACKET_SIZE: packets.append( Packet(packet.src, packet.dest, True, False, block[:utils.MAX_PACKET_SIZE])) block = block[utils.MAX_PACKET_SIZE:] packets.append( Packet(packet.src, packet.dest, True, packet.is_fin, block)) for pack in packets: self.send(pack, self.address_to_port[packet.dest]) else: packet_to_send = packet packet_for_later = None if (packet.src, packet.dest) not in self.buffers.keys(): self.buffers[(packet.src, packet.dest)] = {} self.buffers[(packet.src, packet.dest)]["packets"] = [] self.buffers[(packet.src, packet.dest)]["length"] = 0 # Checking if packet needs to be split if packet.size() + self.buffers[ (packet.src, packet.dest)]["length"] > WanOptimizer.BLOCK_SIZE: diff = WanOptimizer.BLOCK_SIZE - self.buffers[ (packet.src, packet.dest)]["length"] # Result should not be True ever packet_to_send = Packet(packet.src, packet.dest, packet.is_raw_data, False, packet.payload[:diff]) packet_for_later = Packet(packet.src, packet.dest, packet.is_raw_data, packet.is_fin, packet.payload[diff:]) # Checking if source, destination has been seen before: self.buffers[(packet.src, packet.dest)]['packets'].append(packet_to_send) self.buffers[(packet.src, packet.dest)]['length'] += packet.size() # Checking if we have met block size limit and must therefore send if packet.is_fin or self.buffers[ (packet.src, packet.dest)]['length'] >= WanOptimizer.BLOCK_SIZE: packets = self.buffers[(packet.src, packet.dest)]['packets'] # Create block block = "" for pack in packets: block += pack.payload # If block is in our self.hash_to_data if block in self.hash_to_data.values(): if packet.dest in self.address_to_port: for pack in packets: self.send(pack, self.address_to_port[pack.dest]) else: for hash_key, block_val in self.hash_to_data.iteritems( ): if block == block_val: hash_packet = Packet(packet.src, packet.dest, False, packet.is_fin, hash_key) break self.send(hash_packet, self.wan_port) # If seeing packets for the first time else: # Store hash and block to self.hash_to_data self.hash_to_data[utils.get_hash(block)] = block # Send the packets for pack in packets: if packet.dest in self.address_to_port: self.send(pack, self.address_to_port[pack.dest]) else: self.send(pack, self.wan_port) # Reset after sending out buffer self.buffers[(packet.src, packet.dest)]['packets'] = [] self.buffers[(packet.src, packet.dest)]['length'] = 0 if packet_for_later: self.buffers[(packet.src, packet.dest)]['packets'] = [ packet_for_later ] self.buffers[( packet.src, packet.dest)]['length'] = packet_for_later.size()
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: packet_to_send = packet packet_for_later = None # Check if in the dictionary, if not add it in if (packet.src, packet.dest) not in self.buffers.keys(): self.buffers[(packet.src, packet.dest)] = {} self.buffers[(packet.src, packet.dest)]["packets"] = [] self.buffers[(packet.src, packet.dest)]["length"] = 0 # First check if you need to split the packet up if packet.size() + self.buffers[ (packet.src, packet.dest)]["length"] > WanOptimizer.BLOCK_SIZE: diff = WanOptimizer.BLOCK_SIZE - self.buffers[ (packet.src, packet.dest)]["length"] packet_to_send = Packet(packet.src, packet.dest, packet.is_raw_data, False, packet.payload[:diff]) packet_for_later = Packet(packet.src, packet.dest, packet.is_raw_data, packet.is_fin, packet.payload[diff:]) # Store the packet into the buffer. Only the first part (if there even was a split) of the packet self.buffers[(packet.src, packet.dest)]['packets'].append(packet_to_send) self.buffers[(packet.src, packet.dest)]['length'] += packet_to_send.size() # Check if you CAN send the packet (either receiving fin or the buffer is full) if packet.is_fin or self.buffers[ (packet.src, packet.dest)]['length'] >= WanOptimizer.BLOCK_SIZE: self.send_block( self.buffers[(packet.src, packet.dest)]["packets"], packet.src, packet.dest, packet.is_fin) # Check that the second portion of the packet isn't empty if packet_for_later is not None: self.buffers[(packet.src, packet.dest)]['packets'] = [ packet_for_later ] self.buffers[( packet.src, packet.dest)]['length'] = packet_for_later.size() # If it is empty, initialize a new empty one else: self.buffers[(packet.src, packet.dest)]['packets'] = [] self.buffers[(packet.src, packet.dest)]['length'] = 0 # If it isn't raw data, just send the data over. # If it's going to a CLIENT, send reg data based on HASH # If it's going to a WAN, send HASH over. # Details in helper method else: if packet.dest in self.address_to_port: if packet.payload in self.hash_to_data.keys(): raw_data = self.hash_to_data[packet.payload] self.send_payload_by_splitting(raw_data, packet.src, packet.dest, packet.is_fin) else: if packet.payload in self.hash_to_data.keys(): self.send_packet(packet)
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 2. 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 it's a raw data, always buffer it until it finds delimiter. if packet.is_raw_data: self.add_to_buffer(packet) data = self.collect_from_buffer(packet.src, packet.dest) i = 0 s = 0 while i + 48 <= len(data): lower_13 = utils.get_last_n_bits(utils.get_hash(data[i:i+48]), 13) #if it finds delimiter, then send every possible packet that has delimiter if lower_13 == self.GLOBAL_MATCH_BITSTRING: self.reset_buffer(packet.src, packet.dest) packet.payload = data[i+48:] self.add_to_buffer(packet) block_string = data[s: i+48] h = utils.get_hash(block_string) if h in self.hash_to_data: self.send_hash(h, packet.src, packet.dest, False) else: self.hash_to_data[h] = block_string while len(block_string) > 0: if packet.dest in self.address_to_port: self.send(Packet(packet.src, packet.dest, True, False, block_string[:utils.MAX_PACKET_SIZE]), self.address_to_port[packet.dest]) else: self.send(Packet(packet.src, packet.dest, True, False, block_string[:utils.MAX_PACKET_SIZE]), self.wan_port) block_string = block_string[utils.MAX_PACKET_SIZE:] s = i + 48 i += 1 #fin packet, dump it all if packet.is_fin: data = self.collect_from_buffer(packet.src, packet.dest) self.reset_buffer(packet.src, packet.dest) h = utils.get_hash(data) if h in self.hash_to_data: self.send_hash(h, packet.src, packet.dest, True) else: self.hash_to_data[h] = data self.send_data_fin(data, packet.src, packet.dest) else: # case where you receive a hash from the WAN # look up the raw data and forward it to the correct address on your LAN raw_data = self.hash_to_data[packet.payload] while len(raw_data) > utils.MAX_PACKET_SIZE: self.send(Packet(packet.src, packet.dest, True, False, raw_data[:utils.MAX_PACKET_SIZE]), self.address_to_port[packet.dest]) raw_data = raw_data[utils.MAX_PACKET_SIZE:] self.send(Packet(packet.src, packet.dest, True, packet.is_fin, raw_data), self.address_to_port[packet.dest])
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. """ flow = (packet.src, packet.dest) if not flow in self.flow_to_buffer: self.flow_to_buffer[flow] = "" current_buff = self.flow_to_buffer[flow] if packet.dest in self.address_to_port: # The packet is destined to one of the clients connected to this middlebox, send the packet there. if packet.is_raw_data: self.send(packet, self.address_to_port[packet.dest]) empty_space = self.BLOCK_SIZE - len(current_buff) if packet.size() < empty_space: self.flow_to_buffer[flow] = current_buff + packet.payload else: hash_data = utils.get_hash(current_buff + packet.payload[:empty_space]) self.cache[hash_data] = current_buff + packet.payload[:empty_space] self.flow_to_buffer[flow] = packet.payload[empty_space:] # packet already in cache else: data = self.cache[packet.payload] self.packetize_send(flow, packet.is_fin, data, self.address_to_port[packet.dest]) if packet.is_fin: current_buff = self.flow_to_buffer[flow] if (len(current_buff) > 0): hash_data = utils.get_hash(current_buff) self.cache[hash_data] = current_buff del self.flow_to_buffer[flow] #send it across the WAN else: empty_space = self.BLOCK_SIZE - len(current_buff) if packet.size() < empty_space: self.flow_to_buffer[flow] = current_buff + packet.payload else: # buffer is full, send data hash_data = utils.get_hash(current_buff + packet.payload[:empty_space]) if hash_data not in self.cache: self.cache[hash_data] = current_buff + packet.payload[:empty_space] self.packetize_send(flow, False, current_buff + packet.payload[:empty_space], self.wan_port) else: self.send(Packet(packet.src, packet.dest, False, False, hash_data), self.wan_port) # update buffer self.flow_to_buffer[flow] = packet.payload[empty_space:] if packet.is_fin: source = flow[0] destination = flow[1] current_buff = self.flow_to_buffer[flow] data_hash = utils.get_hash(current_buff) if data_hash not in self.cache: if (len(current_buff) > 0): self.cache[data_hash] = current_buff self.packetize_send(flow, True, current_buff, self.wan_port) else: hash_packet = Packet(source, destination, False, True, data_hash) # set is_fin flag self.send(hash_packet, self.wan_port) # delete the flow del self.flow_to_buffer[flow]
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. """ #always buffer raw data until ready to send (full buffer or FIN packet) #this raw data could be from someone on your LAN or from the WAN, either way #you buffer and forward to the right address when the buffer is filled #or the packet is a FIN packet if packet.is_raw_data: current_buffer_size = self.buffer_size( self.get_buffer(packet.src, packet.dest)) if current_buffer_size + packet.size() < self.BLOCK_SIZE: self.add_to_buffer(packet) #fin packet, dump it all if packet.is_fin: to_send = self.collect_from_buffer(packet.src, packet.dest) self.reset_buffer(packet.src, packet.dest) full_data_string = "".join(to_send) h = utils.get_hash(full_data_string) if h in self.hash_to_data: self.send_hash(h, packet.src, packet.dest, True) else: self.hash_to_data[h] = to_send self.send_block(to_send, packet.src, packet.dest, True) else: # buffer full, time to send to_send = self.collect_from_buffer(packet.src, packet.dest) #split up this packet's payload: the first few bytes become #added to this block, the rest get stuffed in later boundary = self.BLOCK_SIZE - current_buffer_size high_half = packet.payload[0:boundary] low_half = packet.payload[boundary:] to_send.append(high_half) self.reset_buffer(packet.src, packet.dest) #Send the first block full_data_string = "".join(to_send) h = utils.get_hash(full_data_string) if h in self.hash_to_data: self.send_hash(h, packet.src, packet.dest) else: self.hash_to_data[h] = to_send self.send_block(to_send, packet.src, packet.dest) #buffer the rest of the packet, else hash/send it as a second block if FIN if not packet.is_fin: self.add_to_buffer( Packet(packet.src, packet.dest, True, False, low_half)) else: h2 = utils.get_hash(low_half) if h2 in self.hash_to_data: self.send_hash(h2, packet.src, packet.dest, True) else: self.send_block([low_half], packet.src, packet.dest, True) else: # case where you receive a hash from the WAN # look up the raw data and forward it to the correct address on your LAN if packet.payload not in self.hash_to_data: print("REEEEE THIS SHOULDN'T HAPPEN!" + str(packet)) raw_data = self.hash_to_data[packet.payload] self.send_block(raw_data, packet.src, packet.dest, packet.is_fin)
def send_hash(self, data_hash, src, dest, is_fin=False): packet = Packet(src, dest, False, is_fin, data_hash) if dest in self.address_to_port: self.send(packet, self.address_to_port[packet.dest]) else: self.send(packet, self.wan_port)
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 2. 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. """ src, dest = packet.src, packet.dest; # Check if in the dictionary, if not add it in if (packet.src, packet.dest) not in self.buffers.keys(): self.buffers[(packet.src, packet.dest)] = {}; self.buffers[(packet.src, packet.dest)]["unhashed_data"] = ""; self.buffers[(packet.src, packet.dest)]["end"] = 48; if packet.is_raw_data: # print str(self) + str(len(packet.payload)) # Grab data from buffers{}, will write data back in at the end to reduce overhead from while loops unhashed_data = self.buffers[(src, dest)]["unhashed_data"] + packet.payload; end = self.buffers[(src, dest)]["end"]; break_loop = False; # First while loop loops through all of unhashed_data while (break_loop == False): block = unhashed_data[:end]; block_hash = utils.get_hash(block); window_hash = utils.get_hash(block[end-48:end]) block_key = utils.get_last_n_bits(window_hash, 13); entered_inner_loop = False; # Second while loop finds the first block that ends with the delimiter while (block_key != WanOptimizer.GLOBAL_MATCH_BITSTRING and end <= len(unhashed_data)): entered_inner_loop = True; block = unhashed_data[:end]; block_hash = utils.get_hash(block); window_hash = utils.get_hash(block[end-48:end]) block_key = utils.get_last_n_bits(window_hash, 13); end = end + 1; if entered_inner_loop: end = end - 1; # Need to subtract by 1 because we will skip one character # when making consecutive blocks because of end = end + 1. # If we didn't find a valid block, leave the loop if block_key != WanOptimizer.GLOBAL_MATCH_BITSTRING: break_loop = True; break; # If we've already hashed this block, then send out the hash # Else we hash it and then send out the whole block if block_hash in self.hash_to_data: hash_packet = Packet(src=packet.src, dest=packet.dest, is_raw_data=False, is_fin=False, payload=block_hash); self.send_packet(hash_packet); else: self.hash_to_data[block_hash] = block; self.split_and_send_data(packet, block); unhashed_data = unhashed_data[end:]; # next window starts at the end of the previous end = 48; # Where we send fin packet if packet.is_fin: # Hash and send whatever data is left in buffer if unhashed_data: hashcode = utils.get_hash(unhashed_data) if hashcode in self.hash_to_data.keys(): hash_packet = Packet(src=packet.src, dest=packet.dest, is_raw_data=False, is_fin=False, payload=hashcode); self.send_packet(hash_packet); else: self.split_and_send_data(packet, unhashed_data); self.hash_to_data[utils.get_hash(unhashed_data)] = unhashed_data; # Send empty fin packet packet.is_fin = True; packet.is_raw_data = True; packet.payload = "" self.send_packet(packet); unhashed_data = ""; end = 48; self.buffers[(src, dest)]["unhashed_data"] = unhashed_data; self.buffers[(src, dest)]["end"] = end; else: if packet.payload in self.hash_to_data.keys(): prev_payload = packet.payload; unhashed_data = self.buffers[(src, dest)]["unhashed_data"]; # If there is unhashed data, hash it and send it out if unhashed_data: hashcode = utils.get_hash(unhashed_data) if hashcode in self.hash_to_data.keys(): hash_packet = Packet(src=packet.src, dest=packet.dest, is_raw_data=False, is_fin=False, payload=hashcode); self.send_packet(hash_packet) else: self.hash_to_data[utils.get_hash(unhashed_data)] = unhashed_data; self.split_and_send_data(packet, unhashed_data); self.buffers[(src, dest)]["unhashed_data"] = ""; self.buffers[(src, dest)]["end"] = 48; if packet.dest in self.address_to_port: raw_data = self.hash_to_data[prev_payload] self.split_and_send_data(packet, raw_data) if packet.is_fin: packet.payload = ""; packet.is_raw_data = True; self.send_packet(packet); else: self.send_packet(packet)
def send_with_hash(hasch, is_fin): #send hash packet is_raw_data = False hash_packet = Packet(packet_key[0], packet_key[1], is_raw_data, is_fin, hasch) self.send(hash_packet, self.wan_port)