def HRECV(self, fsm, net, mac): """HRECV state; manage incoming traffic from MAC protocol. :param net: Associated `NET`. :param mac: Associated `MAC`. Based on the value of `htype`, this method will invoke the appropriate message handler to classify and handle traffic from `mac` to `net`. """ if isinstance(net, Reference): net = net._deref if isinstance(mac, Reference): mac = mac._deref errmsg = "[ARP]: Must be connected to (%s,%s) to RECV!"%(net,mac) assert self.connected(net) and self.connected(mac), errmsg # get message from rxport rxport = self.getport((mac, "RX")) yield rxport.recv(fsm, 1) errmsg = "[ARP]: Error in receiving from mac 'RX' port in RECV!" assert fsm.acquired(rxport) and (len(fsm.got)==1), errmsg # send packet to appropriate message handler p = fsm.got[0] fname = "ARPTX.%s(%s)"%(self.traceid, p.traceid) if (mac.htype==const.ARP_HTYPE_ETHERNET): f = FSM(name=fname) f.goto(self.ETHRECV, net, mac, p) f.start() else: f = FSM(name=fname) f.goto(self.HERRRECV, net, mac, p) f.start() # continue in HRECV yield fsm.goto(self.HRECV, net, mac)
def RECV(self, fsm): """RECV state; monitor traffic from lower layer. This state receives packets on `Port` 'RXD'. It then uses `ptype` to determine which packet handler should process the new data packet, and spawns a new thread to run the handler. :note: Currently, IPv4 is the only protocol type supported. Non-IPv4 packets are passed to `ERRRECV` for handling. """ # error messages rxderror = "[ROUTING]: Error occurred while receiving " + \ "traffic from RXD port!" pkterror = "[ROUTING]: Invalid packet from lower layer!" # get packet from lower layer yield self.RXD.recv(fsm, 1) assert fsm.acquired(self.RXD) and (len(fsm.got)==1), rxderror p = fsm.got[0] if isinstance(p, Reference): p = p._deref assert ANNO.supported(p), pkterror # use appropriate recv function based on ptype if (self.ptype == const.ARP_PTYPE_IP): f = FSM.launch(self.IPRECV, p) # IP ptype else: f = FSM.launch(self.ERRRECV, p) # unknown ptype # continue in RECV yield fsm.goto(self.RECV)
def PSEND(self, fsm, net, mac): """PSEND state; Manage outgoing traffic from network protocol. Based on the value of `ptype`, this method will invoke the appropriate message handler to forward traffic from `net` to `mac`. """ if isinstance(net, Reference): net = net._deref if isinstance(mac, Reference): mac = mac._deref errmsg = "[ARP]: Must be connected to (%s,%s) to SEND!"%(net,mac) assert self.connected(net) and self.connected(mac), errmsg # get message from rxport rxport = self.getport((net,"RX")) yield rxport.recv(fsm, 1) errmsg = "[ARP]: Error in receiving from net 'RX' port in SEND!" assert fsm.acquired(rxport) and (len(fsm.got)==1), errmsg # send packet to appropriate message handler p = fsm.got[0] fname = "ARPTX.%s(%s)"%(self.traceid, p.traceid) if (net.ptype==const.ARP_PTYPE_IP): f = FSM(name=fname) f.goto(self.IPSEND, net, mac, p) f.start() else: f = FSM(name=fname) f.goto(self.PERRSEND, net, mac, p) f.start() # continue in PSEND yield fsm.goto(self.PSEND, net, mac)
def __init__(self, period=0, width=25, **kwargs): """Constructor. :param period: Duration between each output update. :param width: Number of updates per line. """ self.period = period self.width = width self._tic = 0 kwargs['initstate'] = self.MON FSM.__init__(self, **kwargs)
def LISTEN(self, fsm, cif, rxport): """Listen to traffic sent by `ChannelInterface` on `rxport`.""" assert (self.hasport(rxport) is not None), \ "[CHANNEL]: Cannot listen on invalid Port!" # get data from rxport yield rxport.recv(fsm, 1) assert fsm.acquired(rxport) and (len(fsm.got)==1), \ "[CHANNEL]: Error occurred during LISTEN on %s!"%(rxport) # forward on all outgoing edge p, u = fsm.got[0], cif neighbors = [str(c.traceid) for c in self.graph.neighbors(u) ] errmsg = "[CHANNEL]: Cannot forward non-Packet!" assert isinstance(p, Packet), errmsg for v in self.graph[u]: cmodel = self.graph[u][v]['model'] errmsg = "[CHANNEL]: Cannot find corresponding TX Port!" assert (self.hasport((v, "TX") ) is not None) # if channel model for link is not active -> ignore packet if not cmodel.active: continue # filter packets using channel model drop = self.apply_filter(cmodel, p, u, v) if drop: self.log("drp",p,drop=drop,src=u.traceid,dst=v.traceid) continue # continue with next link # copy and mark annotations c, txport = p.copy(), self.getport((v, "TX") ) c.setanno('cif-src', u, ref=True) c.setanno('cif-dst', v, ref=True) # apply channel model propdelay = None r = self.apply_model(cmodel, c, u, v) if cmodel.verbose>CHANNEL_VERBOSE: cmodel.log_forward(c, src=u.traceid, dst=v.traceid) if ANNO.supports(r, 'cm-delay'): propdelay = r.getanno('cm-delay') # forward to destination if (r is None): self.log("drp",c,action="dropped by %s"%(cmodel.traceid) ) elif (propdelay>0): # apply propagation delay f = FSM() f.goto(self.FORWARD, txport, [r]) f.start(delay=propdelay) else: # send immediately (no delay) yield txport.send(fsm, [r]) if self.verbose>CHANNEL_VERBOSE: self.log("fwd", p, dest="%s"%(neighbors) ) # continue in LISTEN yield fsm.goto(self.LISTEN, cif, rxport)
def LISTEN(self, fsm): """LISTEN state; monitor `radio` and manage packet detection.""" r = self.radio assert isinstance(r, Radio), "[DOT11A]: Cannot find radio in LISTEN!" while fsm.active(): # check rxenergy -> set csbusy? rxenergy = r.rxenergy() rxhigh = r.inreceive and (r.rxenergy() > DOT11A_CSTHRESHOLD) if rxhigh: self.set_csbusy(rxenergy="high, %.2f dBm" % (rxenergy), rxbuffer=[x.traceid for x in r.rxbuffer]) else: self.set_csidle(rxenergy="%.2f dBm" % (rxenergy)) # monitor events and RXD port yield self.RXD.recv(fsm, 1, renege=(r.rxdata, r.rxdone, r.txdata, r.txdone, self.detect)) # RXD -> ignore incoming packets in LISTEN if fsm.acquired(self.RXD): assert len(fsm.got) == 1, ( "[DOT11A]: Received unexpected " + "number of packets from 'RXD' port in LISTEN state!" ) p = fsm.got[0] self.log_drop(p, drop="not detected in LISTEN") # rxdata -> start DETECT thread if r.rxdata in fsm.eventsFired: p = r.rxdata.signalparam fname = "detect(%s)" % (p._id) ### XXX #### f = FSM() # f = self.newchild(fname, FSM, tracename=fname.upper() ) f.goto(self.DETECT, p) f.start() # detect -> set csbusy -> goto DECODE if self.detect in fsm.eventsFired: p = self.detect.signalparam rxenergy = "%.2f dBm" % (r.rxenergy()) sinr = "%.2f dB" % (self.sinr(p)) self.set_csbusy(p, detect=True, rxenergy=rxenergy) danno = "dot11a-detect" errmsg = "[DOT11A]: Cannot find 'dot11a-detect' " + "annotation in detected packet!" assert ANNO.supports(p, danno) and p.getanno(danno), errmsg # yield hold, fsm, 0 self.log("detect", p, rxenergy=rxenergy, sinr=sinr, rxbuffer=[x.traceid for x in r.rxbuffer]) yield fsm.goto(self.DECODE, p) # ignore otherwise ignore = r.txdata in fsm.eventsFired ignore = ignore or (r.txdone in fsm.eventsFired) ignore = ignore or (r.rxdone in fsm.eventsFired) if ignore: pass return
def maintain(self, p, nexthop): """Start or udpate route maintenace on IP+DSR packet. :note: This assumes a IP+DSR packet was passed to it. """ # error messages errornexthop = "[DSR]: maintain() found invalid nexthop!" dropbuffer = "maintenance buffer overflow" dropttl = "TTL expired" # get IP/DSR/Option parameters ip, dsr = p[IP], p[DSRPacket] addr, src, dst, ttl = self.address, ip.src, ip.dst, ip.ttl # TTL expired? if (ttl<1): self.log_drop(p, drop=dropttl) return # start or continue route maintenance if nexthop in self.maintbuffer: # add packet to end of maintenance buffer buff = self.maintbuffer[nexthop]['buffer'] if (len(buff)<self.RexmtBufferSize): buff.append(ip) self.debug("MAINTBUFF", ip) else: self.log_drop(ip, drop=dropbuffer) else: # add new buffer and launch thread to maintain it self.maintbuffer[nexthop] = {'buffer':[ip]} f = FSM.launch(self.MAINT, nexthop)
def RXRREP(self, fsm, ip, dsr, opt): """RXRREP state; handle route reply message. :note: Assumes `checkiprecv()` passed. """ self.debug("RXRREP", ip) # get IP/DSR/Option parameters src, dst = ip.src, ip.dst addr, target = self.address, opt.addresses[-1] rreponly = (dsr.nextheader==const.IP_PROTO_NONE) # only keep RREP options and update route cache assert (dst==addr) dsr.options = [o for o in dsr.options if (getdsr_rrep(o))] self.cacheroute(ip) #assert (self.hasroute(target)) assert self.safe((self.hasroute(target))) # verify route to target # get entry from Route Request Table and forward packets rre = self.rrt.getentry(target) while rre.sendbuffer: p = rre.sendbuffer.pop(0) f = FSM.launch(self.IPRECOVER, p, addr, target) self.rrt.delentry(target) # more than RREP? if not rreponly: # remove DSR options and reclassify dsr.options = [] yield fsm.goto(self.IRECV, ip)
def RECV(self, fsm): """Manage upstream (or incoming) traffic. This method spawn worker processes to simulate the capture of each packet. These worker processes manage 'cif-drp' and 'cif-collision' annotations; along with simulating packet 'cif-duration'. """ yield self.RXD.recv(fsm, 1) errmsg = "[CHANNELIF]: RECV() error occurred during recv() from RXD!" assert fsm.acquired(self.RXD) and (len(fsm.got)==1), errmsg # start capture thread for p in fsm.got: w = FSM() w.goto(self.CAPTURE, p) w.start(prior=True) # continue in RECV yield fsm.goto(self.RECV) assert False, "State transition failed!"
def SEND(self, fsm): """SEND state; monitor traffic from upstream protocol and pass to routing algorithm. This state uses the 'net-src' and 'net-dst' annotations to determine the addresses of the source and destination. If not available, these values default to the local `address` and `broadcast` address. This state then uses `ptype` to determine which packet handler should process the new data packet, and spawns a new thread to run the handler. :note: Currently, IPv4 is the only protocol type supported. Non-IPv4 packets are passed to `ERRSEND` for handling. Also, this state will check for loopback addresses and send them back to the upper layer protocol. """ # error messages rxuerror = "[ROUTING]: Error occurred receiving from RXU port!" pkterror = "[ROUTING]: Invalid packet from upper layer!" # get packet to send from upperlayer yield self.RXU.recv(fsm, 1) assert fsm.acquired(self.RXU) and (len(fsm.got)==1), rxuerror p = fsm.got[0] if isinstance(p, Reference): p = p._deref assert ANNO.supported(p), pkterror # process new data packet addr = self.address src, dst = addr, self.broadcast if p.hasanno('net-src'): src = p.getanno('net-src') if p.hasanno('net-dst'): dst = p.getanno('net-dst') r = self.set_sendanno(p, src, dst) # send loopback back to upper layer if (dst==addr): self.log_loopback(p, src=src, dst=dst) yield self.TXU.send(fsm, [p]) yield fsm.goto(self.SEND) # continue in SEND # otherwise -> use appropriate send function based on ptype if (self.ptype == const.ARP_PTYPE_IP): f = FSM.launch(self.IPSEND, r, src, dst) # IP ptype else: f = FSM.launch(self.ERRSEND, r, src, dst) # unknown ptype # continue in SEND yield fsm.goto(self.SEND)
def RXRREQ(self, fsm, ip, dsr, opt): """RXRREQ state; process route request option. :note: Assumes `checkiprecv()` passed. """ self.debug("RXRREQ", ip) # error messages droprreq = "in source route or not new RREQ" # get IP/DSR/Option parameters addr, src, dst = self.address, ip.src, ip.dst ID, target = opt.identification, opt.target rreqonly = (dsr.nextheader==const.IP_PROTO_NONE) # check if target reached if (addr==target): # send RREP [and process remaining packet] path = [a for a in opt.addresses] + [target] f = FSM.launch(self.TXRREP, addr, src, opt, path) # strip RREQ and set dst=target dsr.options = [o for o in dsr.options if (not getdsr_rreq(o))] ip.dst = target # check for other options rerr, rrep = getdsr_rerr(dsr), getdsr_rrep(dsr) if rerr: yield fsm.goto(self.RXRERR, ip, dsr, rerr) elif rrep: yield fsm.goto(self.RXRREP, ip, dsr, rrep) # RREQ only -> HALT and discard packet if rreqonly: yield fsm.stop() # otherwise -> remove DSR options and reclassify dsr.options = [] yield fsm.goto(self.IRECV, ip) # otherwise -> attempt to forward RREQ assert (addr!=target) inroute = (src==addr) or (addr in opt.addresses) newrreq = self.rrt.addcache(src, ID, target) # check if RREQ should be dropped if (inroute or (not newrreq)): self.log_drop(ip, drop=droprreq) yield fsm.stop() # HALT and discard packet # update options and forward RREQ ip.ttl = ip.ttl - 1 opt.addresses = [a for a in opt.addresses] + [addr] yield fsm.goto(self.IPFORWARD, ip)
def MAINT(self, fsm, nexthop, rexmt=0, send=True): """MAINT state; perform route maintenace for nexthop. :note: Assume maintenance buffer contains valid IP+DSR packets. """ yield hold, fsm, 0 # yield to other threads #assert (nexthop in self.maintbuffer) assert self.safe((nexthop in self.maintbuffer)) # error messages droproute = "broken route" # get maintenance parameters buff = self.maintbuffer[nexthop]['buffer'] if (len(buff)<1): del self.maintbuffer[nexthop] yield fsm.stop() # HALT and stop maintenance # get head of buffer p = buff[0] ip, dsr = p[IP], p[DSRPacket] addr, src, dst = self.address, ip.src, ip.dst # check if path is still valid opt, path = getdsr_srcroute(ip), None # one hop away? if opt: segsleft = opt.segsleft # more than one hop? path = [a for a in opt.addresses[-segsleft:]] pathok = self.hasroute(dst, path) # if path is broken -> drop or resend if not pathok: p = buff.pop(0) if (addr==src): f = FSM.launch(self.IPRECOVER, p, src, dst) else: self.log_drop(p, drop=droproute) yield fsm.goto(self.MAINT, nexthop) # continue with next packet # otherwise -> send head of buffer? if send: f = FSM.launch(self.IPDELIVER, ip, nexthop) # wait for link-level feedback (ACK/DROP) mac, feedback = self.mac, None yield waitevent, fsm, (mac.ackdata, mac.drpdata) if (mac.ackdata in fsm.eventsFired): p = mac.ackdata.signalparam if self.issame(ip, p): feedback = "ackdata" elif (mac.drpdata in fsm.eventsFired): p = mac.drpdata.signalparam if self.issame(ip, p): feedback = "drpdata" # process feedback if (feedback=="ackdata"): p = buff.pop(0) yield fsm.goto(self.MAINT, nexthop) # continue with next packet elif (feedback=="drpdata"): rexmt += 1 norexmt = (rexmt>self.MaxMaintRexmt) # rexmt not exceeded -> try again if not norexmt: self.debug("REXMT%d"%(rexmt), ip) yield fsm.goto(self.MAINT, nexthop, rexmt) # otherwise -> broken link!! etype = DSR_ERROR_NODE_UNREACHABLE esrc, unreachable = self.address, nexthop self.debug("DROPDATA", src=esrc, unreachable=unreachable) self.removelink(esrc, unreachable) # signal broken link self.sndfail.signal(esrc) # clear out maintenance buffer errdst = [] while buff: p = buff.pop(0) ip, dsr = p[IP], p[DSRPacket] src, dst = ip.src, ip.dst # send RERR (for non-RREP messages) rrep = getdsr_rrep(ip) sendrerr = (not rrep) and (src not in errdst) # recover packet or send RERR if (addr==src): f = FSM.launch(self.IPRECOVER, p, src, dst) elif sendrerr: errdst.append(src) # send RERR to sources for edst in errdst: f = FSM.launch(self.TXRERR, esrc, edst, etype, unreachable) # continue to allow graceful shutdown yield fsm.goto(self.MAINT, nexthop) else: # feedback not for me -> continue waiting yield fsm.goto(self.MAINT, nexthop, rexmt, send=False)
def TXRREQ(self, fsm, target, options=[], rexmt=0): """TXRREQ state; create and send route request. :param target: Target for route discovery. :param options: Additional DSR options [default=None]. :note: This state is persistent. It will keep trying to send until """ # error messages rreqerror = "[DSR]: Error getting RREQ Table entry!" droprexmt = "max rexmt exceeded" drophasrt = "route already exists" # pause for jitter jitter = random.uniform(0,1)*self.BroadcastJitter yield hold, fsm, jitter # check route and rexmt count if self.hasroute(target): self.log("RREQSTOP", target=target, rexmt=rexmt, drop=drophasrt) rre = self.rrt.getentry(target) while rre.sendbuffer: p = rre.sendbuffer.pop(0) f = FSM.launch(self.IPRECOVER, p, self.address, target) self.rrt.delentry(target) # signal that RREQ has finished self.finrreq.signal(target) yield fsm.stop() # HALT and stop sending RREQ if (rexmt>self.rrt.MaxRequestRexmt): self.log("RREQDROP", target=target, rexmt=rexmt, drop=droprexmt) rre = self.rrt.getentry(target) while rre.sendbuffer: p = rre.sendbuffer.pop(0) self.log_drop(p, drop=droprexmt) self.rrt.delentry(target) # signal that RREQ has been abandoned self.drprreq.signal(target) yield fsm.stop() # HALT and stop sending RREQ # get RREQ parameters sendrreq = self.rrt.sendrreq(target) rre = self.rrt.getentry(target) tleft = rre.timeleft() # get parameters for logging kwargs = {'rexmt':rexmt, 'nbuffered': len(rre.sendbuffer)} kwargs['options'] = [o.tracename for o in options] kwargs['jitter'] = time2msec(jitter) kwargs['timeleft'] = time2msec(tleft) # cannot send RREQ? -> RREQ is busy, drop attempt if not sendrreq: self.debug("RREQBUSY", target=target, **kwargs) yield fsm.stop() # HALT and allow other RREQ to finish # otherwise -> send RREQ ID, ttl = rre.ID, rre.ttl # create DSR+RREQ+options nextheader = self.getproto(None) dsr = DSRPacket(nextheader=nextheader) rreq = DSROPT_RREQ(identification=ID, target=target) dsr.options = [rreq] + [o for o in options] # create IP+DSR proto = self.getproto(dsr) src, dst = self.address, self.broadcast ip = IP(src=src, dst=dst, proto=proto, ttl=ttl) ip.add_payload(dsr) # send RREQ -> wait for timeout, then rexmt self.debug("TXRREQ", ip, target=target, **kwargs) f = FSM.launch(self.IPDELIVER, ip, dst) # signal that RREQ has been sent to target self.sndrreq.signal(target) # wait for send RREQ to timeout before trying again yield hold, fsm, tleft yield fsm.goto(self.TXRREQ, target, options, rexmt+1)