Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
 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)
Example #6
0
 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
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
    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!"
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
    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)