///@note this function is a recent inclusion, but it's not used. Don't use it /// until new ALP is stable, if it is still here. OT_WEAK void alp_load_header(ot_queue* appq, alp_record* rec) { rec->flags = q_readbyte(appq); rec->plength = q_readbyte(appq); rec->id = q_readbyte(appq); rec->cmd = q_readbyte(appq); //q_readstring(appq, (ot_u8*)&rec->flags, 4); //works only on packed structs }
OT_WEAK void alp_breakdown_udp_tmpl(ot_queue* in_q, void* data_type) { ot_int udp_data_length; udp_data_length = q_readshort(in_q); ((udp_tmpl*)data_type)->data_length = udp_data_length; ((udp_tmpl*)data_type)->dst_port = q_readbyte(in_q); ((udp_tmpl*)data_type)->src_port = q_readbyte(in_q); ((udp_tmpl*)data_type)->data = q_markbyte(in_q, udp_data_length); }
OT_WEAK void alp_breakdown_ack_tmpl(ot_queue* in_q, void* data_type) { ot_int ack_id_count; ot_int ack_id_length; ack_id_count = q_readbyte(in_q); ack_id_length = q_readbyte(in_q); ((ack_tmpl*)data_type)->count = (ot_u8)ack_id_count; ((ack_tmpl*)data_type)->length = (ot_u8)ack_id_length; ((ack_tmpl*)data_type)->list = q_markbyte(in_q, ack_id_count*ack_id_length); }
OT_WEAK void alp_breakdown_dialog_tmpl(ot_queue* in_q, void* data_type) { ((dialog_tmpl*)data_type)->timeout = q_readbyte(in_q); if (((dialog_tmpl*)data_type)->timeout & 0x80) { ((dialog_tmpl*)data_type)->channels = q_readbyte(in_q); ((dialog_tmpl*)data_type)->chanlist = \ q_markbyte(in_q, ((dialog_tmpl*)data_type)->channels); } }
ot_int sub_fileheaders( alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in ) { ot_int data_out = 0; vlBLOCK file_block = (vlBLOCK)((cmd_in >> 4) & 0x07); /// Only run if respond bit is set! if (respond) { while ((data_in > 0) && sub_qnotfull(respond, 6, alp->outq)) { vaddr header; ot_bool allow_output = True; data_in--; // one for the file id allow_output = (ot_bool)(vl_getheader_vaddr(&header, file_block, \ q_readbyte(alp->inq), VL_ACCESS_R, NULL) == 0); if (allow_output) { q_writeshort_be(alp->outq, vworm_read(header + 4)); // id & mod q_writeshort(alp->outq, vworm_read(header + 0)); // length q_writeshort(alp->outq, vworm_read(header + 2)); // alloc data_out += 6; } } //alp->BOOKMARK_IN = (void*)sub_testchunk(data_in); } return data_out; }
OT_WEAK void alp_breakdown_query_tmpl(ot_queue* in_q, void* data_type) { ot_u8 query_length; ot_u8 query_code; ot_u8* query_mask; query_code = q_readbyte(in_q); query_length = q_readbyte(in_q); query_mask = NULL; if (query_code & 0x80) { query_mask = q_markbyte(in_q, query_length); } ((query_tmpl*)data_type)->code = query_code; ((query_tmpl*)data_type)->length = query_length; ((query_tmpl*)data_type)->mask = query_mask; ((query_tmpl*)data_type)->value = q_markbyte(in_q, query_length); }
ot_int sub_fileperms( alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in ) { ot_int data_out = 0; vlBLOCK file_block = (vlBLOCK)((cmd_in >> 4) & 0x07); ot_u8 file_mod = ((cmd_in & 0x02) ? VL_ACCESS_W : VL_ACCESS_R); /// Loop through all the listed file ids and process permissions. while ((data_in > 0) && sub_qnotfull(respond, 2, alp->outq)) { ot_u8 file_id = q_readbyte(alp->inq); ot_bool allow_write = respond; vaddr header; data_in--; // one for the file id if (file_mod == VL_ACCESS_W ) { /// run the chmod and return the error code (0 is no error) data_in--; // two for the new mod file_mod = vl_chmod(file_block, file_id, q_readbyte(alp->inq), user_id); } else if (allow_write) { /// Get the header address and return mod (offset 5). The root user /// (NULL) is used because this is only for reading filemod. /// Note: This is a hack that is extremely optimized for speed allow_write = (ot_bool)(vl_getheader_vaddr(&header, file_block, file_id, \ VL_ACCESS_R, NULL) == 0); if (allow_write) { Twobytes filemod; filemod.ushort = vworm_read(header + 4); //shortcut to idmod, hack-ish but fast file_mod = filemod.ubyte[1]; } } if (allow_write) { /// load the data onto the output, if response enabled q_writebyte(alp->outq, file_id); q_writebyte(alp->outq, file_mod); data_out += 2; } } /// return number of bytes put onto the output (always x2) //alp->BOOKMARK_IN = (void*)sub_testchunk(data_in); return data_out; }
OT_WEAK void alp_breakdown_routing_tmpl(ot_queue* in_q, void* data_type) { ot_u8 id_length; ot_u8 code_mask; ((routing_tmpl*)data_type)->hop_code = q_readbyte(in_q); ((routing_tmpl*)data_type)->hop_ext = q_readbyte(in_q); alp_breakdown_id_tmpl(in_q, (void*)&((routing_tmpl*)data_type)->dlog); if (((routing_tmpl*)data_type)->hop_code > 1) { code_mask = (((routing_tmpl*)data_type)->hop_ext != 0) << 7; id_length = q_readbyte(in_q); ((routing_tmpl*)data_type)->orig.length = id_length; code_mask |= (id_length != 0) << 6; code_mask |= (id_length == 2) << 4; ((routing_tmpl*)data_type)->orig.value = q_markbyte(in_q, id_length); id_length = q_readbyte(in_q); ((routing_tmpl*)data_type)->dest.length = id_length; code_mask |= (id_length != 0) << 5; code_mask |= (id_length == 2) << 4; ((routing_tmpl*)data_type)->dest.value = q_markbyte(in_q, id_length); ((routing_tmpl*)data_type)->hop_code |= code_mask; } }
/// Not currently supported, always returns "unrestorable" error ot_int sub_filerestore(alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in ) { ot_int data_out = 0; //vlBLOCK file_block = ((cmd_in >> 4) & 0x07); while ((data_in > 0) && sub_qnotfull(respond, 2, alp->outq)) { ot_u8 err_code = 0x03; ot_u8 file_id = q_readbyte(alp->inq); data_in -= 1; if (respond) { q_writebyte(alp->outq, file_id); q_writebyte(alp->outq, err_code); data_out += 2; } } //alp->BOOKMARK_IN = (void*)sub_testchunk(data_in); return data_out; }
ot_int sub_filedata( alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in ) { vlFILE* fp; ot_u16 offset; ot_u16 span; ot_int data_out = 0; ot_bool inc_header = (ot_bool)((cmd_in & 0x0F) == 0x0C); vlBLOCK file_block = (vlBLOCK)((cmd_in >> 4) & 0x07); ot_u8 file_mod = ((cmd_in & 0x02) ? VL_ACCESS_W : VL_ACCESS_R); ot_queue* inq = alp->inq; ot_queue* outq = alp->outq; sub_filedata_TOP: while (data_in > 0) { vaddr header; ot_u8 err_code; ot_u8 file_id; ot_u16 limit; //alp->BOOKMARK_IN = inq->getcursor; //alp->BOOKMARK_OUT = NULL; file_id = q_readbyte(inq); offset = q_readshort(inq); span = q_readshort(inq); limit = offset + span; err_code = vl_getheader_vaddr(&header, file_block, file_id, file_mod, user_id); file_mod = ((file_mod & VL_ACCESS_W) != 0); //fp = NULL; // A. File error catcher Stage // (In this case, gotos make it more readable) /// Make sure file header was retrieved properly, or goto error if (err_code != 0) { goto sub_filedata_senderror; } /// Make sure file opens properly, or goto error fp = vl_open_file(header); if (fp == NULL) { err_code = 0xFF; goto sub_filedata_senderror; } /// Make sure offset is within file bounds, or goto error if (offset >= fp->alloc) { err_code = 0x07; goto sub_filedata_senderror; } if (limit > fp->alloc) { limit = fp->alloc; err_code = 0x08; } // B. File Writing or Reading Stage // Write to file // 1. Process error on bad ALP parameters, but still do partial write // 2. offset, span are adjusted to convey leftover data // 3. miscellaneous write error occurs when vl_write fails if (file_mod) { for (; offset<limit; offset+=2, span-=2, data_in-=2) { if (inq->getcursor >= inq->back) { goto sub_filedata_overrun; } err_code |= vl_write(fp, offset, q_readshort_be(inq)); } } // Read from File // 1. No error for bad read parameter, just fix the limit // 2. If inc_header param is set, include the file header in output // 3. Read out file data else { ot_u8 overhead; //limit = (limit > fp->length) ? fp->length : limit; overhead = 6; overhead += (inc_header != 0) << 2; if ((outq->putcursor+overhead) >= outq->back) { goto sub_filedata_overrun; } q_writeshort_be(outq, vworm_read(header + 4)); // id & mod if (inc_header) { q_writeshort(outq, vworm_read(header + 0)); // length q_writeshort(outq, vworm_read(header + 2)); // alloc data_out += 4; } q_writeshort(outq, offset); q_writeshort(outq, span); data_out += 6; for (; offset<limit; offset+=2, span-=2, data_out+=2) { if ((outq->putcursor+2) >= outq->back) { goto sub_filedata_overrun; } q_writeshort_be(outq, vl_read(fp, offset)); } } // C. Error Sending Stage sub_filedata_senderror: if ((respond != 0) && (err_code | file_mod)) { if ((outq->putcursor+2) >= outq->back) { goto sub_filedata_overrun; } q_writebyte(outq, file_id); q_writebyte(outq, err_code); q_markbyte(inq, span); // go past any leftover input data data_out += 2; } data_in -= 5; // 5 bytes input header vl_close(fp); } // Total Completion: // Set bookmark to NULL, because the record was completely processed //alp->BOOKMARK_IN = NULL; return data_out; // Partial or Non Completion: // Reconfigure last ALP operation, because it was not completely processed ///@todo Bookmarking is obsolete, because the way Chunking is done has /// been revised. Chunked records must be contiguous. ALP-Main will not /// call this app, and thus not call this function, until the message-end /// bit is detected, therefore meaning that all data is received and /// contiguous. This overrun block, thus, should only check the flags for /// chunking, bypass them, and loop back to the top of this function. sub_filedata_overrun: vl_close(fp); ///@todo alp_next_chunk(alp); // { // ot_u8* scratch; // inq->getcursor = (ot_u8*)alp->BOOKMARK_IN; // scratch = inq->getcursor + 1; // *scratch++ = ((ot_u8*)&offset)[UPPER]; // *scratch++ = ((ot_u8*)&offset)[LOWER]; // *scratch++ = ((ot_u8*)&span)[UPPER]; // *scratch = ((ot_u8*)&span)[LOWER]; // } return data_out; }
ot_int sub_parse_request(m2session* session) { ot_int score = 0; ot_u8 cmd_opcode; //ot_u8 nack = 0; /// 1. Universal Comm Processing <BR> /// - Load CCA type & CSMA disable from command extension <BR> /// - Load NA2P or A2P dialog type from command code m2qp.cmd.code = q_readbyte(&rxq); m2qp.cmd.ext = (m2qp.cmd.code & 0x80) ? q_readbyte(&rxq) : 0; dll.comm.csmaca_params = m2qp.cmd.ext & (M2_CSMACA_CAMASK | M2_CSMACA_NOCSMA); dll.comm.csmaca_params |= m2qp.cmd.code & M2_CSMACA_ARBMASK; cmd_opcode = m2qp.cmd.code & M2OP_MASK; /// 2. All Requests contain the dialog template, so load it. <BR> /// - [ num resp channels ] [ list of resp channels] <BR> /// - if number of resp channels is 0, use the current channel { ot_u8 timeout_code = q_readbyte(&rxq); dll.comm.rx_timeout = otutils_calc_timeout(timeout_code); // original contention period dll.comm.tc = dll.comm.rx_timeout; // contention period counter if (timeout_code & 0x80) { dll.comm.tx_channels = q_readbyte(&rxq); dll.comm.tx_chanlist = q_markbyte(&rxq, dll.comm.tx_channels); } else { dll.comm.tx_channels = 1; dll.comm.tx_chanlist = &dll.comm.scratch[0]; dll.comm.scratch[0] = session->channel; } } /// 3. Handle Command Queries (filtering) <BR> /// Multicast and anycast addressed requests include queries if (m2np.header.addr_ctl & 0x80) { score = sub_process_query(session); } /// 4. If the query is good (sometimes this is trivial): <BR> /// - Prepare the response header (same for all responses) <BR> /// - Run command-specific dialog data processing if (score >= 0) { q_empty(&txq); // Flush TX Queue if (m2qp.cmd.ext & M2CE_NORESP) { session->netstate |= M2_NETFLAG_SCRAP; } else { ot_u8 addressing; session->netstate &= ~M2_NETSTATE_TMASK; session->netstate |= M2_NETSTATE_RESPTX; addressing = ext_get_m2appflags(); addressing |= m2np.header.addr_ctl & 0x30; // make unicast, retain VID & NLS m2np_header(session, addressing, 0); // Create M2QP header q_writebyte(&txq, (M2TT_RESPONSE | cmd_opcode)); // Write Cmd code byte } switch ((cmd_opcode>>1) & 7) { case 0: case 1: break; case 2: sub_opgroup_shell(); break; case 3: case 4: sub_opgroup_collection(); break; case 5: break; case 6: sub_opgroup_datastream(); break; case 7: sub_ack_datastream(); break; } }
ot_int sub_parse_response(m2session* session) { /// Only Gateways and Subcontrollers do anything with the response. Generally, /// Gateways might have some sort of logging in the callbacks. Response types /// include: (1) Normal responses to NA2P standard dialog, (2) Arbitrated /// responses to A2P multicast dialog, (3) Responses that are part of 2, 3, 4, /// or 5-way datastream session. ot_u8 test; ot_u8 cmd_opcode; /// Make sure response command opcode matches the last request's opcode /// (this ensures that the response is to our request). cmd_opcode = m2qp.cmd.code & 0x0F; test = q_readbyte(&rxq) & 0x0F; if (test == cmd_opcode) { # if ((M2_FEATURE(DATASTREAM) == ENABLED) && (OT_FEATURE(ALP) == ENABLED)) /// Manage Responses to Request and Propose Datastream if ((cmd_opcode - M2OP_DS_REQUEST) <= 1) { ot_u16 ds_total_bytes = q_readshort(&rxq); // might be removed ot_u8 fr_per_pkt = q_readbyte(&rxq); // might be removed ot_u8 num_frames = rxq.getcursor[0]; // might be removed rxq.getcursor += ((cmd_opcode & 1) == 0); m2dp.out_rec.flags = ALP_FLAG_MB; // ds beginning // Run Callback (if enabled). Callback can override processing // (Callback is not compiled when callbacks are disabled) test = (ot_u8)M2QP_CALLBACK(DSPKT); if ( test ) { m2dp_dsproc(); } ///@todo Might put in some type of return scoring, later return (ot_int)test; } # if (OT_FEATURE(M2QP_CALLBACKS) == ENABLED) else if (cmd_opcode == 10) { test = (ot_u8)M2QP_CALLBACK(DSACK); if ( test ) { ///@todo Prepare the next stream packet } return (ot_int)test; } # endif else if (((m2qp.cmd.code & 0x60) == 0x40) && \ ((txq.back - txq.putcursor) > 48) ) # else //((M2_FEATURE(DATASTREAM) == ENABLED) && (OT_FEATURE(ALP) == ENABLED)) if (((m2qp.cmd.code & 0x60) == 0x40) && \ ((txq.back - txq.putcursor) > 48) ) # endif /// If using A2P, put this responder's ID onto the ACK chain <BR> /// - Reserve some room at the back for query data (48 bytes) <BR> /// - Increment "Number of ACKs" on each use (txq.getcursor[0]) <BR> /// - Run the A2P callback (if enabled) { ///@todo check to make sure NumACKs is 0 on 1st run (might be done) ///@todo Might put in some type of return scoring, later txq.getcursor[0]++; q_writestring(&txq, m2np.rt.dlog.value, m2np.rt.dlog.length); test = (ot_u8)M2QP_CALLBACK(A2P); } /// If nothing else, the response is a normal response (NA2P), so run /// the callback as normal else { test = (ot_u8)M2QP_CALLBACK(STANDARD); } /// Make into 0/-1 form for returning return (ot_int)test - 1; } }
OT_WEAK void alp_breakdown_u8(ot_queue* in_q, void* data_type) { *(ot_u8*)data_type = q_readbyte(in_q); }
OT_WEAK void alp_breakdown_error_tmpl(ot_queue* in_q, void* data_type) { ((error_tmpl*)data_type)->code = q_readbyte(in_q); ((error_tmpl*)data_type)->subcode = q_readbyte(in_q); ((error_tmpl*)data_type)->data = in_q->getcursor; ///@todo build a routine for code:subcode --> data length }
OT_WEAK void alp_breakdown_id_tmpl(ot_queue* in_q, void* data_type) { ot_int id_length; id_length = q_readbyte(in_q); ((id_tmpl*)data_type)->length = id_length; ((id_tmpl*)data_type)->value = (id_length==0) ? NULL : q_markbyte(in_q, id_length); }
ot_int network_route_ff(m2session* session) { ot_int route_val; /// Strip CRC (-2 bytes) rxq.front[0] -= 2; /// Acquire Flags and Protocol from the Frame Info Field rxq.getcursor = &rxq.front[3]; session->protocol = (*rxq.getcursor & M2FI_FRTYPEMASK); session->flags = *rxq.getcursor & 0xC0; m2np.header.fr_info = *rxq.getcursor++; /// Treat Non-Mode-2 Protocols /// @note Non-Mode-2 protocols not supported at this time if (m2np.header.fr_info & M2FI_NM2) { return -1; } /// Data Link Layer Security if (m2np.header.fr_info & M2FI_DLLS) { # if (OT_FEATURE(DLL_SECURITY)) ///@todo experimental AES_load_static_key(ISF_ID(user_authentication_key), (ot_u32*)txq.front); AES_keyschedule_dec((ot_u32*)txq.front, (ot_u32*)(txq.front+16)); AES_decrypt(rxq.getcursor, rxq.getcursor, (ot_u32*)(txq.front+16)); # else return -1; # endif } /// Address Control Header (Present in M2NP) /// Session Connection and Dialog Filtering: /// - if unassociated, connect now /// - if already connected, make sure the dialog IDs are equal if (m2np.header.fr_info & M2FI_ENADDR) { if (session->netstate & M2_NETSTATE_CONNECTED) { if (session->dialog_id != q_readbyte(&rxq)) { return -1; } } else { session->netstate |= M2_NETSTATE_CONNECTED; session->subnet = rxq.front[2]; session->dialog_id = q_readbyte(&rxq); } /// Grab global flags from Address Control m2np.header.addr_ctl = q_readbyte(&rxq); session->flags |= m2np.header.addr_ctl & 0x3F; /// Grab Source Address from this packet (dialog address), which is /// converted to the target address in the response. m2np.rt.dlog.length = (m2np.header.addr_ctl & M2_FLAG_VID) ? 2 : 8; m2np.rt.dlog.value = q_markbyte(&rxq, m2np.rt.dlog.length); /// Network Layer Security /// @note Network Layer Security not supported at this time if (m2np.header.addr_ctl & M2_FLAG_NLS) { # if (OT_FEATURE(NL_SECURITY)) # else return -1; # endif } /// If unicasting, the next data is the target address, which will have /// the same length as the source address, and it needs to match this /// device's device ID (VID or UID) if ((m2np.header.addr_ctl & 0xC0) == 0) { session->netstate |= M2_NETFLAG_FIRSTRX; if ( !m2np_idcmp(m2np.rt.dlog.length, q_markbyte(&rxq, m2np.rt.dlog.length)) ) { return -1; } } } /// Vector to the appropriate Network Layer Protocol Parser /// Most network protocols don't do anything except broadcast. M2NP is the /// exception, and it manages various types of routing at the network layer. route_val = -1; switch (session->protocol & M2FI_FRTYPEMASK) { case M2FI_FRDIALOG: case M2FI_FRNACK: { // Reset routing template m2np.rt.hop_code = 0; m2np.rt.hop_ext = 0; m2np.rt.orig.value = NULL; m2np.rt.dest.value = NULL; /// Unicast and Anycast Requests have a routing template /// (Not currently supported, so just move the cursor ahead) if ((m2np.header.addr_ctl & 0x40) == 0) { m2np.rt.hop_code = q_readbyte(&rxq); m2np.rt.orig.length = ((m2np.rt.hop_code & M2HC_VID) != 0) ? 2 : 8; m2np.rt.dest.length = m2np.rt.orig.length; if ((m2np.rt.hop_code & M2HC_EXT) != 0) { m2np.rt.hop_ext = q_readbyte(&rxq); } if ((m2np.rt.hop_code & M2HC_ORIG) != 0) { m2np.rt.orig.value = q_markbyte(&rxq, m2np.rt.orig.length); } if ((m2np.rt.hop_code & M2HC_DEST) != 0) { m2np.rt.dest.value = q_markbyte(&rxq, m2np.rt.dest.length); } } } // Note case fall-through case M2FI_STREAM: { /// M2DP gets parsed just like M2NP, but it uses the Network data /// stored from the last M2NP frame route_val = m2qp_parse_frame(session); break; } case M2FI_RFU: break; } /// Attach footer to response, if necessary if (route_val >= 0) { m2np_footer(session); } # if (OT_FEATURE(M2NP_CALLBACKS) == ENABLED) && \ !defined(EXTF_network_sig_route) m2np.signal.route(route_val, session->protocol); # elif defined(EXTF_network_sig_route) network_sig_route(route_val, session->protocol); # endif return route_val; }
OT_WEAK void alp_breakdown_isfcomp_tmpl(ot_queue* in_q, void* data_type) { ((isfcomp_tmpl*)data_type)->is_series = q_readbyte(in_q); ((isfcomp_tmpl*)data_type)->isf_id = q_readbyte(in_q); ((isfcomp_tmpl*)data_type)->offset = q_readshort(in_q); }