def add_flow(self, link, id, f_tuple, m_tuple): """ We have a newly reported flow from a client. Ensure that the proper data structures are in place for it. In this instance we assume that either end could be firewalled. """ # This is a bit tricky. First, we adopt the convention that Alice is # always the sender for a flow, and Bob the receiver (so for a TCP # session, each end is Alice in one direction and Bob in the other). # Next, observe that both Alice and Bob will send "active-flows" # messages adding the flow. Those might arrive in either order. # Furthermore, Bob might send an "active-flows", then a whole lot of # "recv"s, long before Alice sends her "active-flow" -- so whichever end # sends first needs to trigger the instantiation of the Flows and the # Reconciliator. # XXX There are some nasty special cases we don't currently handle here: # 1. The first packet in a flow is mangled in transit, so the # opening_hashes don't match. The port numbers will sometimes tip us of # to this # 2. The first packet in a flow does not arrive first. This shouldn't # be possible for TCP, but it may well happen with other protocols. f = f_tuple self.global_flow_lock.acquire() try: if m_tuple not in self.flow_matchmaker: errlog.info("Creating flow: %s" % ` print_flow_tuple(f) `) rec = Reconciliator.Reconciliator(f, m_tuple) rec.add_link(link, id, f) # it'll figure out which side we are self.flow_matchmaker[m_tuple] = rec errlog.debug("Matchmaker is now %s" % ` self.flow_matchmaker `) else: rec = self.flow_matchmaker[m_tuple] if rec.add_link(link, id, f): # we have two sides to this flow now if self.config.logging: self.log.new_flow(print_flow_tuple(f_tuple), rec.id) finally: self.global_flow_lock.release() # now register the flow in the link itself link.flow_lock.acquire() try: link.flow_table[id] = (f_tuple, rec) finally: link.flow_lock.release()
def add_flow(self, link, id, f_tuple, m_tuple): """ We have a newly reported flow from a client. Ensure that the proper data structures are in place for it. In this instance we assume that either end could be firewalled. """ # This is a bit tricky. First, we adopt the convention that Alice is # always the sender for a flow, and Bob the receiver (so for a TCP # session, each end is Alice in one direction and Bob in the other). # Next, observe that both Alice and Bob will send "active-flows" # messages adding the flow. Those might arrive in either order. # Furthermore, Bob might send an "active-flows", then a whole lot of # "recv"s, long before Alice sends her "active-flow" -- so whichever end # sends first needs to trigger the instantiation of the Flows and the # Reconciliator. # XXX There are some nasty special cases we don't currently handle here: # 1. The first packet in a flow is mangled in transit, so the # opening_hashes don't match. The port numbers will sometimes tip us of # to this # 2. The first packet in a flow does not arrive first. This shouldn't # be possible for TCP, but it may well happen with other protocols. f = f_tuple self.global_flow_lock.acquire() try: if m_tuple not in self.flow_matchmaker: errlog.info("Creating flow: %s" % `print_flow_tuple(f)`) rec = Reconciliator.Reconciliator(f,m_tuple) rec.add_link(link, id, f) # it'll figure out which side we are self.flow_matchmaker[m_tuple] = rec errlog.debug("Matchmaker is now %s" % `self.flow_matchmaker`) else: rec = self.flow_matchmaker[m_tuple] if rec.add_link(link, id, f): # we have two sides to this flow now if self.config.logging: self.log.new_flow(print_flow_tuple(f_tuple), rec.id) finally: self.global_flow_lock.release() # now register the flow in the link itself link.flow_lock.acquire() try: link.flow_table[id] = (f_tuple, rec) finally: link.flow_lock.release()
def ponder_flow(self, link, alice_id, f_tuple, opening_hash): """ Sanity check before we allow a flow into our data structures. If yes, return a representation of the flow for our matchmaker. If no, return False. """ # XXX decide whether to add promiscuity here self.peer_lock.acquire() try: self.debug_note("IS THIS RELEVANT to %s? %s %s" % (link.peer[0], `print_flow_tuple(f_tuple)`, `self.peer_ips.keys()`), seriousness=-1) # link_ip is the ip we expect to find for this client inside its flows # match_ip is the ip we want to use for matchmaking match_ip = s.inet_aton(link.peer[0]) if link.alice_firewalled: self.debug_note("..firewalled..", seriousness=-1) link_ip = link.peers_private_ip else: self.debug_note("..not firewalled..%s" % \ `link.alice_firewalled`, seriousness=-1) link_ip = link.peer[0] assert s.inet_aton(link_ip) self.debug_note("link and match: %s %s" % (link_ip,`match_ip`), seriousness=-1) ip1bin = f_tuple[FlowTuple.src_ip] ip1 = s.inet_ntoa(ip1bin) ip2bin = f_tuple[FlowTuple.dest_ip] ip2 = s.inet_ntoa(ip2bin) if ip1 == link_ip and ip2 in self.peer_ips: return (match_ip, ip2bin, opening_hash) if ip2 == link_ip and ip1 in self.peer_ips: return (ip1bin, match_ip, opening_hash) finally: self.peer_lock.release() link.flow_lock.acquire() try: link.flow_table[alice_id] = None finally: link.flow_lock.release() return False
def add_link(self, link, id, f_tuple): "Figure out whether this link is alice or bob, and remember it" if f_tuple == None: log.error("add_link should not be called with f_tuple=None!!!!") return False self.lock.acquire() try: ip = s.inet_aton(link.peer[0]) if ip == self.m_tuple[0]: self.src_links.append((link, id)) self.src_flow = f_tuple if len(self.src_links) != 1: link.debug_note("Duplicate src_links: %s" % ` self.src_links `) elif ip == self.m_tuple[1]: self.dest_links.append((link, id)) self.dest_flow = f_tuple if len(self.dest_links) != 1: link.debug_note("Duplicate dest_links: %s" % ` self.dest_links `) else: link.protocol_error( "Argh, confused about links and reconciliators!\n") if self.dest_links and self.src_links: skew1 = max( [l.get_clock_dispersion() for l, id in self.src_links]) skew2 = max( [l.get_clock_dispersion() for l, id in self.dest_links]) self.max_clock_skew = (skew1 + skew2) + clock_safety_margin if not (self.src_flow and self.dest_flow): # This is debugging for "weird error"s log.error("Was about to ready a one-sided flow!!!") return False self.ready = True log.debug("We now have both sides of flow %s", print_flow_tuple(self.flow)) return True # have both sides else: log.debug("We currently only have one side of flow: %s", print_flow_tuple(self.flow)) return False finally: self.lock.release()
def add_link(self, link, id, f_tuple): "Figure out whether this link is alice or bob, and remember it" if f_tuple == None: log.error("add_link should not be called with f_tuple=None!!!!") return False self.lock.acquire() try: ip = s.inet_aton(link.peer[0]) if ip == self.m_tuple[0]: self.src_links.append((link, id)) self.src_flow = f_tuple if len(self.src_links) != 1: link.debug_note("Duplicate src_links: %s" % `self.src_links`) elif ip == self.m_tuple[1]: self.dest_links.append((link, id)) self.dest_flow = f_tuple if len(self.dest_links) != 1: link.debug_note("Duplicate dest_links: %s" % `self.dest_links`) else: link.protocol_error("Argh, confused about links and reconciliators!\n") if self.dest_links and self.src_links: skew1 = max([l.get_clock_dispersion() for l,id in self.src_links]) skew2 = max([l.get_clock_dispersion() for l,id in self.dest_links]) self.max_clock_skew = (skew1 + skew2) + clock_safety_margin if not (self.src_flow and self.dest_flow): # This is debugging for "weird error"s log.error("Was about to ready a one-sided flow!!!") return False self.ready = True log.debug("We now have both sides of flow %s", print_flow_tuple(self.flow)) return True # have both sides else: log.debug("We currently only have one side of flow: %s", print_flow_tuple(self.flow)) return False finally: self.lock.release()
def handle_sent_or_recd(self, link, args, sent): """ Sent and recd messages are conceptually very similar, so this function handles both cases. sent = True|False accordingly. """ flow_id, timestamp, hashes = args link.flow_lock.acquire() try: entry = link.flow_table[flow_id] finally: link.flow_lock.release() if entry: rec = entry[1] else: # This flow is being ignored because it isn't between our peers return False rec.lock.acquire() try: try: if sent: forgeries = rec.sent_by_alice(timestamp, hashes) drops = rec.check_for_drops() if drops: self.debug_note("%d dropped packets!" % len(drops)) else: forgeries = rec.recd_by_bob(timestamp, hashes) except Reconciliator.Dangling: opening_hash = rec.m_tuple[2] link.send_message("dangling-flow", [flow_id, opening_hash]) log.warn("Flow %s is dangling" % `print_flow_tuple(rec.flow)`) link.flow_lock.acquire() try: link.flow_table[flow_id] = None finally: link.flow_lock.release() return except AssertionError: # Various things could cause this... link.protocol_error("Assertion violated while handling sent/recd\n" +\ traceback.format_exc()) raise finally: rec.lock.release() if forgeries: self.spotted_forgeries(forgeries, rec) if sent: s = "+" else: s = "-" if self.config.seriousness_threshold <=0: sys.stdout.write(s) sys.stdout.flush()
def handle_active_flows(self, link, args): "The active_flows message from Alice updates our state of flows." new_flows, deleted_flows = args # Process deleted flows first, because we might want to delete a flow # and recreate it simultaneously if it has been closed and re-SYNed try: if not self.config.keep_reconciliators: self.debug_note("deleting flows: %s" % deleted_flows) for f_id in deleted_flows: self.mm.delete_flow(link, f_id) except: link.protocol_error("Problem with flow list: %s\n" % util.screensafe(new_flows)) raise # Now the new flows: try: for flow in new_flows: f_id = flow[0] assert len(flow[1]) == Protocol.hash_length, \ "hashlen is not %d in %r" % (Protocol.hash_length, flow) if ipids_in_matchmaker: opening_hash = flow[1] else: opening_hash = flow[1][:-2] f_tuple = flow[2] assert len(f_tuple) == 5 assert type(opening_hash) == str match = self.ponder_flow(link, f_id, f_tuple, opening_hash) if match: self.debug_note("YES", seriousness=-1) self.mm.add_flow(link, f_id, f_tuple, match) else: self.debug_note("NO", seriousness=-1) if not self.config.sloppy: self.debug_note("Mysteriously Irrelevant Flow!!!%s" % `(link.peer[0],print_flow_tuple(f_tuple))`) except: errlog.debug("OH NOES %s", traceback.format_exc()) link.protocol_error("Problem with flow list: %s\n" % util.screensafe(new_flows)) raise
def flow_key(f): t = print_flow_tuple(f.flow_tuple) return str(t[0].replace(".","_")) + "_" + str(t[1]) + "__" \ + str(t[2].replace(".","_")) + "_" + str((t[3])) \ + "_" + str(t[4])