def dump(self): x = "" if self.rid != None: x += ("rid:%s" % pb.int_to_bit(self.rid, self.R.C.rid_size)) if self.dtag != None: if len(x) != 0: x += " " x += ("dtag:%s" % pb.int_to_bit(self.dtag, self.R.dtag_size)) if self.win != None: if len(x) != 0: x += " " x += ("w:%d" % self.win) if self.fcn != None: if len(x) != 0: x += " " x += ("fcn:%s" % pb.int_to_bit(self.fcn, self.R.fcn_size)) if self.mic != None: if len(x) != 0: x += " " x += ("mic:%s" % pb.int_to_bit(self.mic, self.R.C.mic_size)) x += ("(0x%s)" % "".join(["%02x"%((self.mic>>i)&0xff) for i in [24,16,8,0]])) if self.cbit != None: if len(x) != 0: x += " " x += ("cbit:%d" % self.cbit) if self.bitmap != None: if len(x) != 0: x += " " x += ("bitmap:%s" % pb.int_to_bit(self.bitmap, self.R.bitmap_size)) if self.payload != None: if len(x) != 0: x += " " x += ("payload:%s" % self.payload) # return x
def parse_ack(self, recvbuf, peer): if self.R.mode == SCHC_MODE.NO_ACK: raise AssertionError( "parse_ack() must not be called in NO-ACK mode.") # # XXX here, must check the peer expected. # if self.state.get() == STATE.SEND_ALL0: fgh = sfh.frag_sender_rx_all0_ack(recvbuf, self.R, self.dtag, self.win) elif self.state.get() == STATE.SEND_ALL1: fgh = sfh.frag_sender_rx_all1_ack(recvbuf, self.R, self.dtag, self.win) else: raise TypeError( "state must be either sent_all0 or sent_all1 when parse_ack() is called. but, state=%d" % self.state.get()) if fgh.cbit == 1: self.logger(1, "cbit is on.") self.missing = 0 else: # get the missing fragments. if fgh.bitmap == None: raise ValueError("no bitmap found") # set new bitmap for retransmission. self.missing = self.bitmap & ~fgh.bitmap # save the local bitmap for retransmission self.missing_prev = self.missing self.logger(1, "bitmap tx:", pb.int_to_bit(self.bitmap, self.R.bitmap_size)) self.logger(1, "bitmap rx:", pb.int_to_bit(fgh.bitmap, self.R.bitmap_size)) self.logger(1, "missing :", pb.int_to_bit(self.missing, self.R.bitmap_size)) # if self.missing: if self.state.get() == STATE.SEND_ALL0: return self.state.set(STATE.RETRY_ALL0), fgh elif self.state.get() == STATE.SEND_ALL1: return self.state.set(STATE.RETRY_ALL1), fgh else: # # The receiver looks to have all fragments that the sender sent. # win_head can be moved to the next. # missing is initialized. self.win_head = self.pos self.__init_window() # # if this is the last window, then set it into DONE state. # Note that in ACK-ON-ERROR, it will be set it into DONE later. if self.state.get() == STATE.SEND_ALL0: return self.state.set(STATE.WIN_DONE), fgh elif self.state.get() == STATE.SEND_ALL1: return self.state.set(STATE.DONE), fgh
def all_fragments_received(self): self.logger(1, "checking all-0 fragments, local bitmap=", pb.int_to_bit(self.bitmap, self.R.bitmap_size)) if self.bitmap == self.R.bitmap_all_1: self.logger(1, "received all fragments.") return True else: self.logger(1, "not received all fragments.") return False
def make_frag(self, dtag, win=None, fcn=None, mic=None, bitmap=None, cbit=None, abort=False, payload=None): ''' payload: bit string of the SCHC fragment payload. ''' # ba = bytearray() pos = 0 # # basic fields. if self.R.rid != None and self.R.C.rid_size: pb.bit_set(ba, 0, pb.int_to_bit(self.R.rid, self.R.C.rid_size), extend=True) pos += self.R.C.rid_size if dtag != None and self.R.dtag_size: pb.bit_set(ba, pos, pb.int_to_bit(dtag, self.R.dtag_size), extend=True) pos += self.R.dtag_size # # extension fields. if win != None and self.R.win_size: pb.bit_set(ba, pos, pb.int_to_bit(win, self.R.win_size), extend=True) pos += self.R.win_size if fcn != None and self.R.fcn_size: pb.bit_set(ba, pos, pb.int_to_bit(fcn, self.R.fcn_size), extend=True) pos += self.R.fcn_size if mic != None and self.R.C.mic_size: pb.bit_set(ba, pos, pb.int_to_bit(mic, self.R.C.mic_size), extend=True) pos += self.R.C.mic_size if cbit != None and self.R.cbit_size: pb.bit_set(ba, pos, pb.int_to_bit(cbit, self.R.cbit_size), extend=True) pos += self.R.cbit_size if bitmap != None and self.R.bitmap_size: pb.bit_set(ba, pos, pb.int_to_bit(bitmap, self.R.bitmap_size), extend=True) pos += self.R.bitmap_size if abort == True: pb.bit_set(ba, pos, pb.int_to_bit(0xff, 8), extend=True) pos += 8 # if payload != None: # assumed that bit_set() has extended to a byte boundary. pb.bit_set(ba, pos, payload, extend=True) # # the abort field is implicit, is not needed to set into the parameter. self.set_param(self.R.rid, dtag, win, fcn, mic, bitmap, cbit, payload) self.packet = ba
def __add_win_mode(self, fgh): ''' except NO_ACK mode ''' f = self.fragment_list.setdefault(fgh.fcn, {}) # empty ALL0 or ALL1 only be acceptable with duplicate FCN. if len(f): if (fgh.payload == None and (self.win_state.get() == STATE.CONT_ALL0 and fgh.fcn == self.R.fcn_all_0)): return self.win_state.set(STATE.CHECK_ALL0) elif (fgh.payload == None and (self.win_state.get() == STATE.CONT_ALL1 and fgh.fcn == self.R.fcn_all_1)): return self.win_state.set(STATE.CHECK_ALL1) else: self.logger(1, "ERROR: got a FCN which was received before.", self.win_state.get(), self.win, fgh.fcn) return STATE.FAIL # add new fragment for the assembling self.fragment_list[fgh.fcn] = fgh.payload # if fgh.fcn == self.R.fcn_all_1: self.bitmap |= 1 else: self.bitmap |= 1<<fgh.fcn self.logger(1, "bitmap=", pb.int_to_bit(self.bitmap, self.R.bitmap_size)) # # assuming that, in each window except the last one, # the bitmap must be filled up by the fragments that sender sends. if self.win_state.get() in [STATE.INIT, STATE.CONT]: if fgh.fcn == self.R.fcn_all_0: # immediately after receiving the all-0 return self.win_state.set(STATE.CHECK_ALL0) elif fgh.fcn == self.R.fcn_all_1: # immediately after receiving the all-1 return self.win_state.set(STATE.CHECK_ALL1) elif self.win_state.get() == STATE.INIT: # fnc is neigher all-0 nor all-1, if it's first fragment. return self.win_state.set(STATE.CONT) else: # otherwise, return STATE.CONT elif self.win_state.get() in [STATE.ALL0_NG, STATE.CONT_ALL0]: # same as the CONT_ALL1 state. if fgh.fcn == self.R.fcn_all_0: return self.win_state.set(STATE.CHECK_ALL0) elif self.win_state.get() == STATE.ALL0_NG: return self.win_state.set(STATE.CONT_ALL0) else: return STATE.CONT_ALL0 elif self.win_state.get() == STATE.ALL0_OK: # same as the CONT_ALL1 state. if fgh.fcn == self.R.fcn_all_0: return self.win_state.get() elif self.win_state.get() in [STATE.ALL1_NG, STATE.CONT_ALL1]: # It doesn't need to check the payload size (i.e. ALL-1 empty). # If ALL-1 message is received at CONT_ALL1 state, # it just changes the state into CHECK_ALL1 anyway # doesn't change the state before it gets any ALL-1 message. if fgh.fcn == self.R.fcn_all_1: return self.win_state.set(STATE.CHECK_ALL1) elif self.win_state.get() == STATE.ALL1_NG: return self.win_state.set(STATE.CONT_ALL1) else: return STATE.CONT_ALL1 elif self.win_state.get() == STATE.SEND_ACK1: # same as the CONT_ALL1 state. if fgh.fcn == self.R.fcn_all_1: return self.win_state.get() else: # in other case. raise ValueError("invalid state=%s" % self.win_state.pprint())
def next_fragment(self, l2_size): ''' l2_size: the caller can specify the size of the payload anytime. XXX Note that it doesn't care the l2_size in retransmitting. if the size is changed in that case, the packet will not be changed as it was. needs to be improved. ''' # compute the max/min payload size. max_pyld_size, min_pyld_size = self.get_payload_base_size(l2_size) # if self.R.mode == SCHC_MODE.NO_ACK and self.state.get( ) == STATE.SEND_ALL1: # no more fragments to be sent return self.state.set(STATE.DONE), None # if self.state.get() == STATE.SEND_ALL0: # it comes here when the timeout happens while waiting for the # ack response from the receiver even though either all-0 was sent. if self.R.mode == SCHC_MODE.ACK_ALWAYS: self.missing = self.missing_prev self.state.set(STATE.RETRY_ALL0) else: # here, for ACK-ON-ERROR self.state.set(STATE.WIN_DONE) pass elif self.state.get() == STATE.SEND_ALL1: # here, the case the sender sent all-1, but no response from the # receiver. self.missing = self.missing_prev self.state.set(STATE.RETRY_ALL1) # if self.state.get() in [STATE.RETRY_ALL0, STATE.RETRY_ALL1]: # if all the missing fragments has been sent, resend the empty ALL. if self.missing == 0: # set the state into the previous one. self.state.back() if self.state.get() == STATE.SEND_ALL0: fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.R.fcn_all_0) elif self.state.get() == STATE.SEND_ALL1: fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.R.fcn_all_1, mic=self.mic) else: AssertionError("invalid state in retransmission %s" % self.state.get()) # return self.state.get(), fgh # if there are any missing fragments, # the sender only needs to send missed fragments. # doesn't need to send all-0/all1. if self.R.mode == SCHC_MODE.NO_ACK: raise AssertionError( "no-ack mode must not come here in next_fragment().") # e.g. N=3, Max FCN = 7 # all-0 # bit: 1 2 3 4 5 6 7 # fcn: 6 5 4 3 2 1 0 # all-1 # bit: 1 2 3 4 5 6 7 # fcn: 6 5 7 p, self.missing = pb.bit_find(self.missing, self.R.bitmap_size) self.logger(1, "retransmitting.", "fgh_list=", self.fgh_list.keys(), "p=", p, "missing=", pb.int_to_bit(self.missing, self.R.bitmap_size)) if p == None or p > self.R.max_fcn: raise AssertionError( "in missing check, p must be from 1 to %d" % self.R.max_fcn) if p == self.R.max_fcn: if self.state.get() == STATE.RETRY_ALL0: fgh = self.fgh_list[self.R.fcn_all_0] else: # i.e. SCHC_FRAG_RETRY_ALL1 fgh = self.fgh_list[self.R.fcn_all_1] else: fgh = self.fgh_list[self.R.max_fcn - p] # in others, return CONT, # however don't need to change the internal state. # i.e RETRY_ALL0 or RETRY_ALL1. return STATE.CONT, fgh # # defragment for transmitting. # rest_size = len(self.srcbuf) - self.pos if self.R.mode == SCHC_MODE.NO_ACK: if rest_size > min_pyld_size: if rest_size >= max_pyld_size: pyld_size = max_pyld_size else: pyld_size = rest_size self.fcn = 0 mic = None state = STATE.CONT else: # this is the last fragment. pyld_size = rest_size self.fcn = 1 mic = self.mic state = STATE.SEND_ALL1 # fgh = sfh.frag_sender_tx(self.R, self.dtag, fcn=self.fcn, mic=mic, payload=self.srcbuf[self.pos:self.pos + pyld_size]) self.state.set(state) self.n_frags_sent += 1 self.pos += pyld_size return self.state.get(), fgh else: if self.state.get() == STATE.WIN_DONE: # try to support more than 1 bit wide window # though the bit size is 1 in the draft 7. self.win += 1 self.win &= ((2**self.R.win_size) - 1) self.fcn = self.R.max_fcn else: if self.fcn == None: self.fcn = self.R.max_fcn else: self.fcn -= 1 # in above, just set fcn. # then will check below if the packet is the last one. # if so, set fcn into all-1 at that time.. if rest_size > min_pyld_size: self.bitmap |= 1 << self.fcn if rest_size >= max_pyld_size: pyld_size = max_pyld_size else: pyld_size = rest_size fgh = sfh.frag_sender_tx( self.R, self.dtag, win=self.win, fcn=self.fcn, payload=self.srcbuf[self.pos:self.pos + pyld_size]) if self.fcn == self.R.fcn_all_0: self.state.set(STATE.SEND_ALL0) else: self.state.set(STATE.CONT) else: # this is the last window. self.bitmap |= 1 pyld_size = rest_size self.fcn = self.R.fcn_all_1 fgh = sfh.frag_sender_tx( self.R, self.dtag, win=self.win, fcn=self.fcn, mic=self.mic, payload=self.srcbuf[self.pos:self.pos + pyld_size]) self.state.set(STATE.SEND_ALL1) # # save the local bitmap for retransmission # this is for the case when the receiver will not respond. self.missing_prev = self.bitmap # store the packet for the future retransmission. self.fgh_list[self.fcn] = fgh self.pos += pyld_size return self.state.get(), fgh
def next_fragment(self, l2_size): ''' l2_size: the size of the L2 payload in bytes. Note that l2_size in this method is converted the unit in bits. XXX Note that it doesn't care the l2_size in retransmitting. if the size is changed in that case, the packet will not be changed as it was. needs to be improved. ''' l2_size = l2_size * 8 # NOTE: converted into the unit of bits. # max_payload_size = l2_size - self.header_size if max_payload_size <= 0: raise AssertionError( "L2 size is smaller than the header. hdr={} L2={}".format( self.header_size, l2_size)) # the max_payload_size must be bigger than or equal to the MIC size. # at least, it's ensure that the payload size is enough to put the MIC. if max_payload_size < self.mic_size: raise AssertionError( "L2 size is smaller than the mic size. mic={} L2={}".format( self.mic_size, l2_size)) # if self.R.mode == SCHC_MODE.NO_ACK and self.state.get( ) == STATE.SEND_ALL1: # no more fragments to be sent return self.state.set(STATE.DONE), None # if self.state.get() == STATE.SEND_ALL0: # it comes here when the timeout happens while waiting for the # ack response from the receiver even though either all-0 was sent. if self.R.mode == SCHC_MODE.ACK_ALWAYS: self.missing = self.missing_prev self.state.set(STATE.RETRY_ALL0) else: # here, for ACK-ON-ERROR self.state.set(STATE.WIN_DONE) pass elif self.state.get() == STATE.SEND_ALL1: # here, the case the sender sent all-1, but no response from the # receiver. self.missing = self.missing_prev self.state.set(STATE.RETRY_ALL1) # if self.state.get() in [STATE.RETRY_ALL0, STATE.RETRY_ALL1]: # if all the missing fragments has been sent, resend the empty ALL. if self.missing == 0: # set the state into the previous one. self.state.back() if self.state.get() == STATE.SEND_ALL0: fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.R.fcn_all_0) elif self.state.get() == STATE.SEND_ALL1: fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.R.fcn_all_1, mic=self.mic) else: AssertionError("invalid state in retransmission %s" % self.state.get()) # return self.state.get(), fgh # if there are any missing fragments, # the sender only needs to send missed fragments. # doesn't need to send all-0/all1. if self.R.mode == SCHC_MODE.NO_ACK: raise AssertionError( "no-ack mode must not come here in next_fragment().") # e.g. N=3, Max FCN = 7 # all-0 # bit: 1 2 3 4 5 6 7 # fcn: 6 5 4 3 2 1 0 # all-1 # bit: 1 2 3 4 5 6 7 # fcn: 6 5 7 p, self.missing = pb.bit_find(self.missing, self.R.bitmap_size) self.logger(1, "retransmitting.", "fgh_list=", self.fgh_list.keys(), "p=", p, "missing=", pb.int_to_bit(self.missing, self.R.bitmap_size)) if p == None or p > self.R.max_fcn: raise AssertionError( "in missing check, p must be from 1 to %d" % self.R.max_fcn) if p == self.R.max_fcn: if self.state.get() == STATE.RETRY_ALL0: fgh = self.fgh_list[self.R.fcn_all_0] else: # i.e. SCHC_FRAG_RETRY_ALL1 fgh = self.fgh_list[self.R.fcn_all_1] else: fgh = self.fgh_list[self.R.max_fcn - p] # in others, return CONT, # however don't need to change the internal state. # i.e RETRY_ALL0 or RETRY_ALL1. return STATE.CONT, fgh # # defragment for transmitting. # if self.R.mode == SCHC_MODE.NO_ACK: margin = max_payload_size - self.rest_payload_size if margin < 0: pyld_size = max_payload_size self.fcn = 0 state = STATE.CONT else: # check whether there is enough size to put the MIC. if margin < self.mic_size: pyld_size = self.rest_payload_size self.fcn = 0 state = STATE.CONT else: # this is the last fragment. # margin is equal to or bigger than mic_size. pyld_size = self.rest_payload_size self.fcn = 1 self.mic = True state = STATE.SEND_ALL1 # payload = pb.bit_get(self.srcbuf, self.pos, pyld_size) if self.mic is None: self.append_tx_list(payload) else: self.append_tx_list(payload, self.mic_size) self.logger(1, "calculating mic") for i in self.tx_payload_list: self.logger(2, "fragment =", i) self.mic = self.R.C.mic_func.get_mic( pb.bit_to("".join(self.tx_payload_list), ljust=True)) # fgh = sfh.frag_sender_tx(self.R, self.dtag, fcn=self.fcn, mic=self.mic, payload=payload) self.state.set(state) self.n_frags_sent += 1 self.pos += pyld_size self.rest_payload_size -= pyld_size if self.rest_payload_size < 0: raise AssertionError("rest_payload_size becomes less than 0") return self.state.get(), fgh # # from here, ACK_ALWAYS or ACK_ON_ERROR # if self.state.get() == STATE.WIN_DONE: # try to support more than 1 bit wide window # though the bit size is 1 in the draft 7. self.win += 1 self.win &= ((2**self.R.win_size) - 1) self.fcn = self.R.max_fcn else: if self.fcn == None: self.fcn = self.R.max_fcn else: self.fcn -= 1 # in above, just set fcn. # then will check below if the packet is the last one. # if so, set fcn into all-1 at that time.. # XXX needs to revisit and improve the logic. margin = max_payload_size - self.rest_payload_size if margin < 0: self.bitmap |= 1 << self.fcn pyld_size = max_payload_size payload = pb.bit_get(self.srcbuf, self.pos, pyld_size) self.append_tx_list(payload) fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.fcn, payload=payload) if self.fcn == self.R.fcn_all_0: self.state.set(STATE.SEND_ALL0) else: self.state.set(STATE.CONT) elif margin < self.mic_size: self.bitmap |= 1 << self.fcn pyld_size = self.rest_payload_size payload = pb.bit_get(self.srcbuf, self.pos, pyld_size) self.append_tx_list(payload) fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.fcn, payload=payload) if self.fcn == self.R.fcn_all_0: self.state.set(STATE.SEND_ALL0) else: self.state.set(STATE.CONT) else: # this is the last window. self.bitmap |= 1 pyld_size = self.rest_payload_size self.fcn = self.R.fcn_all_1 payload = pb.bit_get(self.srcbuf, self.pos, pyld_size) self.append_tx_list(payload, self.mic_size) # self.logger(1, "calculating mic") for i in self.tx_payload_list: self.logger(2, "fragment =", i) self.mic = self.R.C.mic_func.get_mic( pb.bit_to("".join(self.tx_payload_list), ljust=True)) # fgh = sfh.frag_sender_tx(self.R, self.dtag, win=self.win, fcn=self.fcn, mic=self.mic, payload=payload) self.state.set(STATE.SEND_ALL1) # # save the local bitmap for retransmission # this is for the case when the receiver will not respond. self.missing_prev = self.bitmap # store the packet for the future retransmission. self.fgh_list[self.fcn] = fgh self.rest_payload_size -= pyld_size self.pos += pyld_size return self.state.get(), fgh