def retry(self, count=None): """Update retry count and set backoff parameters. :param count: If specified, `retrycount` will be set to this value; otherwise, increment `retrycount`. Slot value `cslot` is set to a random integer between [0, CW), where the contention window CW is defined as: CW = CWmin + 2^`retrycount` """ if count is None: count = self.retrycount + 1 cwsize = min(self.cwmax, self.cwmin * (2**count) ) self.cslot = random.randint(cwsize) self.retrycount = count # update RETRY flag if first retry if ((count==1) and self.isdot11data(self.datatosend)): pkt = self.get_dot11data(self.datatosend) dst = pkt.addr1 errmsg = "[DCF]: Cannot retry() with broadcast packet!" assert (dst != self.broadcast), errmsg # update retry field pkt.FCfield |= DOT11_FC_RETRY self.datatosend = crcupdate(pkt) if count>0: self.log("retry%d"%(count), self.datatosend, retrycount=count, retrylimit=self.retrylimit)
def TXRSH(self, fsm): """TXRSH state; overloaded to send RSH prior to DATA.""" assert DCF.isdot11data(self, self.datatosend), \ "[RBAR]: Cannot determine 'datatosend' in TXUCAST!" assert not (self.datatosend.addr1==self.broadcast), \ "[RBAR]: Cannot send broadcast 'datatosend' in TXUCAST!" # update rate annotation data = self.get_dot11data(self.datatosend) dst, src = data.addr1, data.addr2 rate = self.ra.get_rate(dst) data.setanno('phy-rate', rate) if not self.usecsma: # send RSH before sending DATA rsh = self.dot11rsh(addr1=dst, addr2=src) self.send_rsh(rsh, self.datatosend) # recalculate NAV from new rate information nav = self.rshnav(self.datatosend) rsh.ID = nav pkt = crcupdate(rsh) # send and hold for duration rate, length = pkt[RAI].rate, pkt[RAI].length self.log("RSH", pkt, nav=nav, rate=rate, length=length) duration = self.duration(pkt) self.log_send(pkt, nav=nav) yield self.TXD.send(fsm, [pkt]) yield hold, fsm, duration # pause for SIFS before continuing yield hold, fsm, self.sifs # go to DCF.TXUCAST state OLDSTATE = lambda *args, **kwargs: DCF.TXUCAST(self, *args, **kwargs) yield fsm.goto(OLDSTATE)
def get_rshduration(self, force=False): """Calculate duration of RSH message. :param force: If true, ignore any cached value. """ if force or (self._rshduration is None): rsh = self.dot11rsh() pkt = crcupdate(rsh) self._rshduration = self.duration(pkt) return self._rshduration
def get_ackduration(self, force=False): """Calculate duration of ack message. :param force: If true, ignore any cached value. """ if force or (self._ackduration is None): ack = self.dot11ack() pkt = crcupdate(ack) self._ackduration = self.duration(pkt) return self._ackduration
def get_ctsduration(self, force=False): """Calculate duration of CTS message. :param force: If true, ignore any cached value. """ if force or (self._ctsduration is None): cts = self.dot11cts() pkt = crcupdate(cts) self._ctsduration = self.duration(pkt) return self._ctsduration
def TXCTS(self, fsm, rts): """TXCTS state; send CTS response message.""" assert self.isdot11rts(rts), "[DCF]: Cannot find RTS in TXCTS!" addr, addr1 = self.address, rts.addr2 # create CTS pkt = self.dot11cts(addr1=addr1) cts = crcupdate(pkt) # update nav self.send_cts(cts, rts) nav = self.ctsnav(rts) cts.ID = nav pkt = crcupdate(cts) # pause for SIFS yield hold, fsm, self.sifs # send and hold duration duration = self.duration(pkt) self.log_send(pkt, addr=addr, addr1=addr1, nav=nav) yield self.TXD.send(fsm, [pkt]) yield hold, fsm, duration # set NAV and resume yield self.navupdate(fsm, nav*1e-6) yield fsm.goto(self.RESUME)
def TXACK(self, fsm, pkt): """TXACK state; transmit ACK message in response to `data`.""" assert self.isdot11data(pkt), "[DCF]: Cannot find Dot11Data in TXACK!" data = self.get_dot11data(pkt) assert not (data.addr1==self.broadcast), \ "[DCF]: Cannot send ACK for broadcast data!" addr1 = data.addr2 ack = self.dot11ack(addr1=addr1) self.send_ack(ack) pkt = crcupdate(ack) # pause for SIFS yield hold, fsm, self.sifs # send and hold duration duration = self.duration(pkt) self.log_send(pkt, addr1=addr1, duration=duration) yield self.TXD.send(fsm, [pkt]) yield hold, fsm, duration yield fsm.goto(self.RESUME)
def TXDATA(self, fsm, pkt): """TXDATA state; initialize `datatosend` and associated parameters before transitioning to `BACKOFF`.""" assert (self.datatosend is None), \ "[DCF]: 'datatosend' already set in TXDATA!" assert (self.htype==const.ARP_HTYPE_ETHERNET), \ "[DCF]: Unsupported hardware type (%s)!"%(self.htype) assert isinstance(pkt, Ether) and pkt.haslayer(Ether), \ "[DCF]: Got non-Ether packet in TXDATA!" # process Ethernet frame eth = pkt[Ether] addr, src, dst = self.addr, eth.src, eth.dst pkt = crcupdate(eth) # initialize datatosend and other parameters self.datatosend = self.encapsulate(pkt, src=src, dst=dst) isbroadcast = (dst==self.broadcast) self.retry(count=0) if isbroadcast: self.retrycount = self.retrylimit self.datatosend.setanno('mac-txts', now()) # go to BACKOFF yield fsm.goto(self.BACKOFF)
def encapsulate(self, p, src=None, dst=None, **kwargs): """Convenience method to encapsulate an packet in an IEEE 802.11 data header (i.e. `Dot11Data`). :param p: Packet to encapsulate. :param src: Source address [default=`address`] :param dst: Destination address [default=`broadcast`] :param kwargs: Additional keywords passed to `Dot11Data` constructor. :return: Newly created `Dot11Data` packet. :note: This method adds/updates a CRC using `crcupdate()`. """ if src is None: src = self.address if dst is None: dst = self.broadcast addr1, addr2 = dst, src pargs = {'addr1': addr1, 'addr2': addr2} kwargs.update(pargs) data = self.dot11data(**kwargs) data.add_payload(p) pkt = crcupdate(data) return pkt
def ETHSEND(self, fsm, p): """ETHSEND state; send ethernet frame. :param p: Ethernet packet to transmit. By default, this state appends a crc to packet `p` and sends it to `Port` 'TXD' without any address checking. After simulating the duration of the packet as reported by `duration()`, this state execution method returns to `SEND`. """ assert (self.htype==const.ARP_HTYPE_ETHERNET), "[ALOHA]: In ETHSEND," + \ " non-Ethernet hardware type (%s) not allowed!"%(self.htype) assert isinstance(p, Packet) and p.haslayer(Ether), \ "[ALOHA]: ETHSEND cannot handle non-Ether packet!" eth = p[Ether] addr, src, dst, etype = self.address, eth.src, eth.dst, eth.type pkt = crcupdate(eth) duration = self.duration(pkt) self.log_send(pkt, addr=addr, src=src, dst=dst, type=etype, \ duration=time2usec(duration) ) yield self.TXD.send(fsm, [pkt]) yield hold, fsm, duration yield fsm.goto(self.SEND)
def TXRTS(self, fsm): """TXRTS state; send RTS for `datatosend`.""" assert self.isdot11data(self.datatosend), \ "[DCF]: Cannot determine 'datatosend' in TXRTS!" assert not (self.datatosend.addr1==self.broadcast), \ "[DCF]: Cannot send broadcast 'datatosend' in TXRTS!" # create RTS src, dst = self.datatosend.addr2, self.datatosend.addr1 rts = self.dot11rts(addr1=dst, addr2=src) if (self.retrycount>0): rts.FCfield |= DOT11_FC_RETRY # calculate NAV self.send_rts(rts, self.datatosend) nav = self.rtsnav(self.datatosend) rts.ID = nav pkt = crcupdate(rts) # send and hold for duration duration = self.duration(pkt) self.log_send(pkt, src=src, dst=dst, nav=nav, \ duration=time2usec(duration), retry=self.retrycount) yield self.TXD.send(fsm, [pkt]) yield hold, fsm, duration # go to RXCTS yield fsm.goto(self.RXCTS)
def ETHSEND(self, fsm, net, mac, p, dst=None): """ETHSEND state; Encapsulate `p` in Ethernet frame and send to `mac`. :param net: Associated `NET`. :param mac: Associated `MAC`. :param p: Ethernet packet to send. :param dst: Ethernet address of destination [default=`MAC.broadcast`] Upon completion this method returns to `returnstate` with the parameters `net` and `mac`. """ # get parameters for Ethernet packet src, htype = mac.addr, mac.htype if dst is None: dst = mac.broadcast ethertype = self.get_ethertype(p) eth = Ether(src=src, dst=dst, type=ethertype) eth.add_payload(p) # update CRC pkt = eth if USE_CRC32: pkt = crcupdate(eth) # send to TX port self.log_send(pkt, src=src, dst=dst, type=ethertype) txport = self.getport((mac, "TX")) yield txport.send(fsm, [pkt])