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 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 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)