diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java index f641755..7c84951 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/HierarchicalRegrouperForC.java @@ -86,6 +86,7 @@ public class HierarchicalRegrouperForC { // } } } + List reActionSets1 = new ArrayList<>(); for(HierarchicalActionSet a:reActionSets){ HierarchicalActionSet hierarchicalActionSet = purifyActionSet(a); @@ -97,6 +98,9 @@ public class HierarchicalRegrouperForC { } } + + + return reActionSets1; // return reActionSets; } @@ -106,10 +110,11 @@ public class HierarchicalRegrouperForC { hierarchicalActionSet = removeParentForSingle(hierarchicalActionSet); hierarchicalActionSet = removeParentNode(hierarchicalActionSet); return hierarchicalActionSet; +// return actionSet; } private List divideBlocks(HierarchicalActionSet actionSet){ - if (actionSet.getAstNodeType().equals("block")){ + if (actionSet.getAstNodeType().equals("block") || actionSet.getAstNodeType().equals("function")){ List subActions = actionSet.getSubActions(); if (subActions.size() > 1) { boolean b = subActions.stream().allMatch(p -> NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, p.getAstNodeType()).size() == 1 && p.getAstNodeType().equals(subActions.get(0).getAstNodeType()) && p.getAction().getName().equals(subActions.get(0).getAction().getName())); @@ -145,6 +150,53 @@ public class HierarchicalRegrouperForC { // return b; } + + + private HierarchicalActionSet removeBlocks(HierarchicalActionSet actionSet){ + List subActions = actionSet.getSubActions(); + Predicate predicate = x->NodeMap_new.getKeysByValue(NodeMap_new.StatementMap,x.getAstNodeType()).size() == 1 ; + Predicate predicate1 = x->!x.getAstNodeType().equals("block"); + Predicate predicate2 = x->!x.getAstNodeType().equals("then"); + + + Action action = actionSet.getAction(); + if (subActions.size() == 1){ + HierarchicalActionSet subaction = subActions.get(0); + if(!postOrder(subaction).stream().anyMatch(predicate.and(predicate1.and(predicate2)))){ + return actionSet; + } + Action action1 = subaction.getAction(); + //else,then,block + if(action.getClass().equals(action1.getClass()) && action.getName().equals("UPD")) { + + List keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subaction.getAstNodeType()); + if (keysByValue != null && keysByValue.size() == 1) { + + subaction.setParent(null); + return removeBlocks(subaction); + + } +// if(areRelatedActions(action,action1)) { +// if (subaction.getAstNodeType().equals("block")) {//|| subaction.getAstNodeType().equals("then") || subaction.getAstNodeType().equals("else")){ +// List subSubActions = subaction.getSubActions(); +// if (subSubActions.size() == 1) { +// +// HierarchicalActionSet subsubsubAction = subSubActions.get(0); +// List keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subsubsubAction.getAstNodeType()); +// if (keysByValue != null && keysByValue.size() == 1) { +// +// subsubsubAction.setParent(null); +// return removeBlocks(subsubsubAction); +// +// } +// } +// } + } + } + return actionSet; + + } + private HierarchicalActionSet removeParentNode(HierarchicalActionSet actionSet){ List subActions = actionSet.getSubActions(); Action action = actionSet.getAction(); @@ -164,42 +216,6 @@ public class HierarchicalRegrouperForC { return actionSet; } - private HierarchicalActionSet removeBlocks(HierarchicalActionSet actionSet){ - List subActions = actionSet.getSubActions(); - Predicate predicate = x->NodeMap_new.getKeysByValue(NodeMap_new.StatementMap,x.getAstNodeType()).size() == 1 ; - Predicate predicate1 = x->!x.getAstNodeType().equals("block"); - - - Action action = actionSet.getAction(); - if (subActions.size() == 1){ - HierarchicalActionSet subaction = subActions.get(0); - if(!postOrder(subaction).stream().anyMatch(predicate.and(predicate1))){ - return actionSet; - } - Action action1 = subaction.getAction(); - //else,then,block - if(action.getClass().equals(action1.getClass()) && action.getName().equals("UPD")) { -// if(areRelatedActions(action,action1)) { -// if (subaction.getAstNodeType().equals("block")) {//|| subaction.getAstNodeType().equals("then") || subaction.getAstNodeType().equals("else")){ - List subSubActions = subaction.getSubActions(); - if (subSubActions.size() == 1) { - - HierarchicalActionSet subsubsubAction = subSubActions.get(0); - List keysByValue = NodeMap_new.getKeysByValue(NodeMap_new.StatementMap, subsubsubAction.getAstNodeType()); - if (keysByValue != null && keysByValue.size() == 1) { - - subsubsubAction.setParent(null); - return removeBlocks(subsubsubAction); - - } - } -// } - } - } - return actionSet; - - } - private HierarchicalActionSet removeParentForSingle(HierarchicalActionSet actionSet){ List subActions = actionSet.getSubActions(); Action action = actionSet.getAction(); @@ -395,7 +411,12 @@ public class HierarchicalRegrouperForC { // } for (Action act : actions) { - if (act.getNode().equals(parent)) { + if (act instanceof Move){ + if(act.getNode().equals(action.getNode())){ + return act; + } + } + else if (act.getNode().equals(parent)) { if (areRelatedActions(act, action)) { return act; } @@ -481,9 +502,9 @@ public class HierarchicalRegrouperForC { } private boolean areRelatedActions(Action parent, Action child) { - if (parent instanceof Move && !(child instanceof Move)) {// If action is MOV, its children must be MOV. - return false; - } +// if (parent instanceof Move && !(child instanceof Move)) {// If action is MOV, its children must be MOV. +// return false; +// } if (parent instanceof Delete && !(child instanceof Delete)) {// If action is DEL, its children must be DEL. return false; } diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java index 8d161d5..e800348 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestInputCases.java @@ -71,8 +71,9 @@ public class TestInputCases { // List hierarchicalActionSets = getHierarchicalActionSets("wireshark_a9e926_877728_epan#dissectors#packet-ndps.c");//wrong // List hierarchicalActionSets = getHierarchicalActionSets("php-src_041867_5d66c3_ext#pdo#pdo_dbh.c");//wrong // List hierarchicalActionSets = getHierarchicalActionSets("php-src_d810cf_2d8fd4_Zend#zend_builtin_functions.c");//wrong - List hierarchicalActionSets = getHierarchicalActionSets("php-src_c56492_008fb2_sapi#phpdbg#phpdbg.c");//wrong - +// List hierarchicalActionSets = getHierarchicalActionSets("php-src_c56492_008fb2_sapi#phpdbg#phpdbg.c");//wrong + List hierarchicalActionSets = getHierarchicalActionSets("wireshark_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c");//wrong +//php-src_acd809_1d3d39_ext#standard#scanf.c // List hierarchicalActionSets = getHierarchicalActionSets("php-src_67a100_505f61_TSRM#tsrm_virtual_cwd.c");// // List hierarchicalActionSets = getHierarchicalActionSets("cpython_2cf5d3_9a0d7a_Objects#object.c");// @@ -82,10 +83,102 @@ public class TestInputCases { // String s = EDiffHelper.getShapeTree(hierarchicalActionSets.get(0), false).toStaticHashString(); // String s1 = EDiffHelper.getShapeTree(hierarchicalActionSets1.get(0), false).toStaticHashString(); Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),""); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" @TO@ if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ 157870 @LENGTH@ 92\n" + + "---UPD then@@ @TO@ item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ -1 @LENGTH@ 0\n" + + "------MOV block@@item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @TO@ then@@ @AT@ 157973 @LENGTH@ 152\n"); } @Test + public void test_wireshark_96ba09_755c08() throws IOException { + List hierarchicalActionSets = getHierarchicalActionSets("wireshark_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c"); + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" @TO@ if gsm_rr_csn_HL_flag tvb subtree tvb_len bit_offset ++ \"Priority 2\" \"Present\" \"Not present\" item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ 157870 @LENGTH@ 92\n" + + "---UPD then@@ @TO@ item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @AT@ -1 @LENGTH@ 0\n" + + "------MOV block@@item2 = proto_tree_add_bits_item subtree hf_gsm_a_call_prio tvb bit_offset 3 FALSE bit_offset += 3 proto_item_append_text item2 \" for Mobile Identity 2\" @TO@ then@@ @AT@ 157973 @LENGTH@ 152\n"); + + } + @Test + public void test_php_src_cf4a29_40becd() throws IOException {//todo upd mov with delete (get rid of delete + List hierarchicalActionSets = getHierarchicalActionSets("php-src_cf4a29_40becd_main#safe_mode.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 0 elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ if mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 0 elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2519 @LENGTH@ 204\n" + + "---UPD elseif@@elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ elseif if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2651 @LENGTH@ 99\n" + + "------UPD if@@if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename @TO@ if mode == CHECKUID_ALLOW_FILE_NOT_EXISTS php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2651 @LENGTH@ 92\n" + + "---------UPD then@@php_error E_WARNING \"Unable to access %s\" filename @TO@ php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2696 @LENGTH@ 50\n" + + "------------UPD block@@php_error E_WARNING \"Unable to access %s\" filename @TO@ php_error E_WARNING \"Unable to access %s\" filename return 1 @AT@ 2696 @LENGTH@ 50\n" + + "---------------MOV return@@return 1 @TO@ block@@php_error E_WARNING \"Unable to access %s\" filename @AT@ 2756 @LENGTH@ 8\n"); + } + @Test + public void test_cpython_1a8501_c47253() throws IOException {//todo wrong + List hierarchicalActionSets = getHierarchicalActionSets("cpython_1a8501_c47253_Modules#signalmodule.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),""); + } + @Test + public void test_cpython_58fb90_c9e435() throws IOException {//TODO definitely wrong + List hierarchicalActionSets = getHierarchicalActionSets("cpython_58fb90_c9e435_Modules#_stringio.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),2); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),""); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),""); + } + @Test + public void test_cpython_1c76b7_d6056b() throws IOException { + List hierarchicalActionSets = getHierarchicalActionSets("cpython_1c76b7_d6056b_Modules#faulthandler.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD function@@static void faulthandler_thread void * unused PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event PyThread_release_lock thread . cancel_event @TO@ static void faulthandler_thread void * unused PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event @AT@ 10367 @LENGTH@ 223\n" + + "---UPD block@@PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event PyThread_release_lock thread . cancel_event @TO@ PyLockStatus st const char * errmsg PyThreadState * current int ok do thread . running = 0 PyThread_release_lock thread . join_event @AT@ 10413 @LENGTH@ 177\n"); + Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"MOV expr_stmt@@PyThread_release_lock thread . cancel_event @TO@ block@@break; @AT@ 11259 @LENGTH@ 43\n"); + } + @Test + public void test_php_src_acd809_1d3d39() throws IOException { + List hierarchicalActionSets = getHierarchicalActionSets("php-src_acd809_1d3d39_ext#standard#scanf.c"); + Assert.assertEquals(hierarchicalActionSets.size(),1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD switch@@* ch 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @TO@ * ch 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' 'X' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19173 @LENGTH@ 807\n" + + "---UPD block@@'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @TO@ 'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' 'X' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19179 @LENGTH@ 802\n" + + "------INS case@@'X' @TO@ block@@'n' if ! ( flags & SCAN_SUPPRESS ) if numVars zend_uint refcount current = args ][objIndex ++ refcount = ( * current ) -> refcount zval_dtor * current ZVAL_LONG * current long string - baseString ( * current ) -> refcount = refcount ( * current ) -> is_ref = 1 else add_index_long * return_value objIndex ++ string - baseString nconversions ++ continue; 'd' 'D' op = 'i' base = 10 fn = ( long * ) strtol break; 'i' op = 'i' base = 0 fn = ( long * ) strtol break; 'o' op = 'i' base = 8 fn = ( long * ) strtol break; 'x' op = 'i' base = 16 fn = ( long * ) strtol break; 'u' op = 'i' base = 10 flags |= SCAN_UNSIGNED fn = ( long * ) strtoul break; 'f' 'e' 'E' 'g' op = 'f' break; 's' op = 's' break; 'c' op = 's' flags |= SCAN_NOSKIP if 0 == width width = 1 break; '[' op = '[' flags |= SCAN_NOSKIP break; @AT@ 19912 @LENGTH@ 3\n" + + "---------INS expr@@'X' @TO@ case@@'X' @AT@ 19917 @LENGTH@ 3\n" + + "------------INS literal@@'X' @TO@ expr@@'X' @AT@ 19917 @LENGTH@ 3\n"); + } + @Test + public void test_lighttpd14_f27b8d_4a86d3() throws IOException {//TODO not sure + List hierarchicalActionSets = getHierarchicalActionSets("lighttpd1.4_f27b8d_4a86d3_src#connections.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),3); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@400 505 207 break; default: con -> http_status = 501 break; @TO@ 400 414 505 207 break; default: con -> http_status = 501 break; @AT@ 8385 @LENGTH@ 59\n" + + "---INS case@@414 @TO@ block@@400 505 207 break; default: con -> http_status = 501 break; @AT@ 8421 @LENGTH@ 3\n" + + "------INS expr@@414 @TO@ case@@414 @AT@ 8426 @LENGTH@ 3\n" + + "---------INS literal@@414 @TO@ expr@@414 @AT@ 8426 @LENGTH@ 3\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" + + "---UPD expr@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" + + "------UPD call@@log_error_write srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ log_error_write srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23540 @LENGTH@ 109\n" + + "---------UPD argument_list@@srv __FILE__ __LINE__ \"sd\" \"http-header larger then 64k -> disconnected\" chunkqueue_length cq @TO@ srv __FILE__ __LINE__ \"s\" \"oversized request-header -> sending Status 414\" @AT@ 23555 @LENGTH@ 93\n" + + "------------UPD argument@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" + + "---------------UPD expr@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" + + "------------------UPD literal@@\"sd\" @TO@ \"s\" @AT@ 23581 @LENGTH@ 4\n" + + "------------UPD argument@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" + + "---------------UPD expr@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" + + "------------------UPD literal@@\"http-header larger then 64k -> disconnected\" @TO@ \"oversized request-header -> sending Status 414\" @AT@ 23587 @LENGTH@ 45\n" + + "------------DEL argument@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" + + "---------------DEL expr@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" + + "------------------DEL call@@chunkqueue_length cq @AT@ 23634 @LENGTH@ 20\n" + + "---------------------DEL name@@chunkqueue_length @AT@ 23634 @LENGTH@ 17\n" + + "---------------------DEL argument_list@@cq @AT@ 23651 @LENGTH@ 2\n" + + "------------------------DEL argument@@cq @AT@ 23652 @LENGTH@ 2\n" + + "---------------------------DEL expr@@cq @AT@ 23652 @LENGTH@ 2\n" + + "------------------------------DEL name@@cq @AT@ 23652 @LENGTH@ 2\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD expr_stmt@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" + + "---UPD expr@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" + + "------UPD call@@connection_set_state srv con CON_STATE_REQUEST_END @TO@ connection_set_state srv con CON_STATE_HANDLE_REQUEST @AT@ 23741 @LENGTH@ 50\n" + + "---------UPD argument_list@@srv con CON_STATE_REQUEST_END @TO@ srv con CON_STATE_HANDLE_REQUEST @AT@ 23761 @LENGTH@ 29\n" + + "------------UPD argument@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n" + + "---------------UPD expr@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n" + + "------------------UPD name@@CON_STATE_REQUEST_END @TO@ CON_STATE_HANDLE_REQUEST @AT@ 23772 @LENGTH@ 21\n"); + } + @Test + public void test_wireshark_87646b_2cc0fb() throws IOException { + List hierarchicalActionSets = getHierarchicalActionSets("wireshark_87646b_2cc0fb_epan#proto.c");//wrong + Assert.assertEquals(hierarchicalActionSets.size(),1); + } + @Test public void test_php_src_a0a56e_7afbb7() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("php-src_a0a56e_7afbb7_ext#com_dotnet#com_extension.c");//ok Assert.assertEquals(hierarchicalActionSets.size(),1); diff --git a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java index 8fb842a..57ab8e4 100644 --- a/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java +++ b/src/main/java/edu/lu/uni/serval/fixminer/ediff/TestRealCases.java @@ -31,12 +31,12 @@ public class TestRealCases { "------INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" + "---------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" + "---------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" + - "---------INS literal@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" + + "---------INS literal:number@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" + "---INS expr_stmt@@h = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d == 3 || h == 3 printf \"YES\" return 0 @AT@ 553 @LENGTH@ 5\n" + "------INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" + "---------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" + "---------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" + - "---------INS literal@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n"); + "---------INS literal:number@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n"); } @@ -44,17 +44,16 @@ public class TestRealCases { public void test_287_A_14208521_14208532() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("287-A-14208521-14208532.c"); Assert.assertEquals(hierarchicalActionSets.size(),2); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @TO@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 d = 0 h = 0 @AT@ 199 @LENGTH@ 347\n" + - "---INS expr_stmt@@d = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @AT@ 548 @LENGTH@ 5\n" + - "------INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" + - "---------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" + - "---------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" + - "---------INS literal@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n" + - "---INS expr_stmt@@h = 0 @TO@ block@@ if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" 0 @AT@ 553 @LENGTH@ 5\n" + - "------INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" + - "---------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" + - "---------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" + - "---------INS literal@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n"); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"INS expr_stmt@@d = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" return 0 @AT@ 548 @LENGTH@ 5\n" + + "---INS expr@@d = 0 @TO@ expr_stmt@@d = 0 @AT@ 548 @LENGTH@ 5\n" + + "------INS name@@d @TO@ expr@@d = 0 @AT@ 548 @LENGTH@ 1\n" + + "------INS operator@@= @TO@ expr@@d = 0 @AT@ 549 @LENGTH@ 1\n" + + "------INS literal:number@@0 @TO@ expr@@d = 0 @AT@ 550 @LENGTH@ 1\n"); + Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"INS expr_stmt@@h = 0 @TO@ block@@if g ][i ][j == '.' d ++ else h ++ if g ][i ][j + 1 == '.' d ++ else h ++ if g ][i + 1 ][j == '.' d ++ else h ++ if g ][i + 1 ][j + 1 == '.' d ++ else h ++ if d >= 3 || h >= 3 printf \"YES\" return 0 @AT@ 553 @LENGTH@ 5\n" + + "---INS expr@@h = 0 @TO@ expr_stmt@@h = 0 @AT@ 553 @LENGTH@ 5\n" + + "------INS name@@h @TO@ expr@@h = 0 @AT@ 553 @LENGTH@ 1\n" + + "------INS operator@@= @TO@ expr@@h = 0 @AT@ 554 @LENGTH@ 1\n" + + "------INS literal:number@@0 @TO@ expr@@h = 0 @AT@ 555 @LENGTH@ 1\n"); } @@ -91,23 +90,15 @@ public class TestRealCases { @Test public void test_680_A_18343132_18343191() throws IOException { List hierarchicalActionSets = getHierarchicalActionSets("680-A-18343132-18343191.c"); - Assert.assertEquals(hierarchicalActionSets.size(),1); - Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@ int i a ][5 b ][101 0 t 0 temp max 0 for i = 0 i < 5 i ++ scanf \"%d\" & a ][i b ][a ][i ++ t += a ][i for i = 0 i < 5 i ++ if b ][a ][i == 3 max = 3 * a ][i break; for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; printf \"%d\" t - max 0 @TO@ int i a ][5 b ][101 0 t 0 temp max 0 for i = 0 i < 5 i ++ scanf \"%d\" & a ][i b ][a ][i ++ t += a ][i for i = 0 i < 5 i ++ if b ][a ][i >= 3 max = 3 * a ][i break; for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp printf \"%d\" t - max 0 @AT@ 50 @LENGTH@ 295\n" + - "---UPD for@@for i = 0 i < 5 i ++ if b ][a ][i == 3 max = 3 * a ][i break; @TO@ for i = 0 i < 5 i ++ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 158 @LENGTH@ 63\n" + - "------UPD block@@ if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 173 @LENGTH@ 44\n" + - "---------UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 41\n" + - "------------UPD condition@@ b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 13\n" + - "---------------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" + - "------------------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n" + - "---UPD for@@for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ for i = 0 i < 5 i ++ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 221 @LENGTH@ 90\n" + - "------UPD block@@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 236 @LENGTH@ 77\n" + - "---------UPD if@@if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp break; @TO@ if b ][a ][i == 2 temp = a ][i * 2 if temp > max max = temp @AT@ 239 @LENGTH@ 68\n" + - "------------UPD then@@ temp = a ][i * 2 if temp > max max = temp break; @TO@ temp = a ][i * 2 if temp > max max = temp @AT@ 253 @LENGTH@ 50\n" + - "---------------UPD block@@ temp = a ][i * 2 if temp > max max = temp break; @TO@ temp = a ][i * 2 if temp > max max = temp @AT@ 253 @LENGTH@ 57\n" + - "------------------UPD if@@if temp > max max = temp break; @TO@ if temp > max max = temp @AT@ 270 @LENGTH@ 32\n" + - "---------------------UPD then@@ max = temp break; @TO@ max = temp @AT@ 282 @LENGTH@ 18\n" + - "------------------------UPD block@@ max = temp break; @TO@ max = temp @AT@ 282 @LENGTH@ 24\n" + - "---------------------------DEL break@@break; @AT@ 296 @LENGTH@ 6\n"); + Assert.assertEquals(hierarchicalActionSets.size(),2); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 40\n" + + "---UPD condition@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 14\n" + + "------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" + + "---------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n"); + Assert.assertEquals(hierarchicalActionSets.get(1).toString(),"UPD if@@if b ][a ][i == 3 max = 3 * a ][i break; @TO@ if b ][a ][i >= 3 max = 3 * a ][i break; @AT@ 176 @LENGTH@ 40\n" + + "---UPD condition@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 176 @LENGTH@ 14\n" + + "------UPD expr@@b ][a ][i == 3 @TO@ b ][a ][i >= 3 @AT@ 177 @LENGTH@ 14\n" + + "---------UPD operator@@== @TO@ >= @AT@ 184 @LENGTH@ 2\n"); } @@ -146,7 +137,7 @@ public class TestRealCases { "------------UPD index@@][j + 1 @TO@ ][j @AT@ 591 @LENGTH@ 7\n" + "---------------UPD expr@@[j + 1 @TO@ [j @AT@ 591 @LENGTH@ 6\n" + "------------------DEL operator@@+ @AT@ 592 @LENGTH@ 1\n" + - "------------------DEL literal@@1 @AT@ 593 @LENGTH@ 1\n"); + "------------------DEL literal:number@@1 @AT@ 593 @LENGTH@ 1\n"); } @@ -185,7 +176,7 @@ public class TestRealCases { "---------INS expr@@a == 784262 @TO@ condition@@a == 784262 @AT@ 878 @LENGTH@ 11\n" + "------------INS name@@a @TO@ expr@@a == 784262 @AT@ 878 @LENGTH@ 1\n" + "------------INS operator@@== @TO@ expr@@a == 784262 @AT@ 879 @LENGTH@ 2\n" + - "------------INS literal@@784262 @TO@ expr@@a == 784262 @AT@ 881 @LENGTH@ 6\n" + + "------------INS literal:number@@784262 @TO@ expr@@a == 784262 @AT@ 881 @LENGTH@ 6\n" + "------INS then@@printf \"4079\\n\" @TO@ if@@if a == 784262 printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + "---------INS block@@printf \"4079\\n\" @TO@ then@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + "------------INS expr_stmt@@printf \"4079\\n\" @TO@ block@@printf \"4079\\n\" @AT@ 901 @LENGTH@ 15\n" + @@ -195,7 +186,7 @@ public class TestRealCases { "---------------------INS argument_list@@\"4079\\n\" @TO@ call@@printf \"4079\\n\" @AT@ 907 @LENGTH@ 8\n" + "------------------------INS argument@@\"4079\\n\" @TO@ argument_list@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" + "---------------------------INS expr@@\"4079\\n\" @TO@ argument@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n" + - "------------------------------INS literal@@\"4079\\n\" @TO@ expr@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n"); + "------------------------------INS literal:string@@\"4079\\n\" @TO@ expr@@\"4079\\n\" @AT@ 908 @LENGTH@ 8\n"); } @Test @@ -209,14 +200,14 @@ public class TestRealCases { "------------UPD index@@][( n + 1 ) / 2 @TO@ ][( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 15\n" + "---------------UPD expr@@[( n + 1 ) / 2 @TO@ [( n + 1 ) / 2 - 1 @AT@ 778 @LENGTH@ 14\n" + "------------------INS operator@@- @TO@ expr@@[( n + 1 ) / 2 @AT@ 785 @LENGTH@ 1\n" + - "------------------INS literal@@1 @TO@ expr@@[( n + 1 ) / 2 @AT@ 786 @LENGTH@ 1\n" + + "------------------INS literal:number@@1 @TO@ expr@@[( n + 1 ) / 2 @AT@ 786 @LENGTH@ 1\n" + "---INS elseif@@elseif if n == 1 printf \"0\\n\" @TO@ if@@if array ][( n + 1 ) / 2 == x printf \"0\\n\" elseif if last < ( n + 1 ) / 2 printf \"%d\\n\" n - 2 * last elseif if first > ( n + 1 ) / 2 printf \"%d\\n\" 2 * first - n - 1 @AT@ 925 @LENGTH@ 29\n" + "------INS if@@if n == 1 printf \"0\\n\" @TO@ elseif@@elseif if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 22\n" + "---------INS condition@@n == 1 @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 925 @LENGTH@ 6\n" + "------------INS expr@@n == 1 @TO@ condition@@n == 1 @AT@ 926 @LENGTH@ 6\n" + "---------------INS name@@n @TO@ expr@@n == 1 @AT@ 926 @LENGTH@ 1\n" + "---------------INS operator@@== @TO@ expr@@n == 1 @AT@ 927 @LENGTH@ 2\n" + - "---------------INS literal@@1 @TO@ expr@@n == 1 @AT@ 929 @LENGTH@ 1\n" + + "---------------INS literal:number@@1 @TO@ expr@@n == 1 @AT@ 929 @LENGTH@ 1\n" + "---------INS then@@printf \"0\\n\" @TO@ if@@if n == 1 printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + "------------INS block@@printf \"0\\n\" @TO@ then@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + "---------------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" @AT@ 932 @LENGTH@ 12\n" + @@ -226,7 +217,7 @@ public class TestRealCases { "------------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 938 @LENGTH@ 5\n" + "---------------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" + "------------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n" + - "---------------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n"); + "---------------------------------INS literal:string@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 939 @LENGTH@ 5\n"); } @@ -243,7 +234,7 @@ public class TestRealCases { "------------INS expr@@[j @TO@ index@@][j @AT@ 379 @LENGTH@ 2\n" + "---------------INS name@@[j @TO@ expr@@[j @AT@ 379 @LENGTH@ 2\n" + "------INS operator@@= @TO@ expr@@a ][j = 0 @AT@ 381 @LENGTH@ 1\n" + - "------INS literal@@0 @TO@ expr@@a ][j = 0 @AT@ 382 @LENGTH@ 1\n"); + "------INS literal:number@@0 @TO@ expr@@a ][j = 0 @AT@ 382 @LENGTH@ 1\n"); } @@ -261,7 +252,7 @@ public class TestRealCases { "---------------INS expr@@[i @TO@ index@@][i @AT@ 243 @LENGTH@ 2\n" + "------------------INS name@@[i @TO@ expr@@[i @AT@ 243 @LENGTH@ 2\n" + "---------INS operator@@!= @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 245 @LENGTH@ 2\n" + - "---------INS literal@@0 @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 247 @LENGTH@ 1\n"); + "---------INS literal:number@@0 @TO@ expr@@ara ][i >= ara ][k - 1 @AT@ 247 @LENGTH@ 1\n"); } @@ -272,12 +263,12 @@ public class TestRealCases { Assert.assertEquals(hierarchicalActionSets.size(),1); Assert.assertEquals(hierarchicalActionSets.get(0).toString(), "INS else@@else i1 = 0 @TO@ if@@if input ][0 == '.' count = 1 @AT@ 295 @LENGTH@ 11\n" + - "---INS block@@i1 = 0 @TO@ else@@else i1 = 0 @AT@ 295 @LENGTH@ 6\n" + - "------INS expr_stmt@@i1 = 0 @TO@ block@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" + - "---------INS expr@@i1 = 0 @TO@ expr_stmt@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" + - "------------INS name@@i1 @TO@ expr@@i1 = 0 @AT@ 295 @LENGTH@ 2\n" + - "------------INS operator@@= @TO@ expr@@i1 = 0 @AT@ 297 @LENGTH@ 1\n" + - "------------INS literal@@0 @TO@ expr@@i1 = 0 @AT@ 298 @LENGTH@ 1\n"); + "---INS block@@i1 = 0 @TO@ else@@else i1 = 0 @AT@ 295 @LENGTH@ 6\n" + + "------INS expr_stmt@@i1 = 0 @TO@ block@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" + + "---------INS expr@@i1 = 0 @TO@ expr_stmt@@i1 = 0 @AT@ 295 @LENGTH@ 6\n" + + "------------INS name@@i1 @TO@ expr@@i1 = 0 @AT@ 295 @LENGTH@ 2\n" + + "------------INS operator@@= @TO@ expr@@i1 = 0 @AT@ 297 @LENGTH@ 1\n" + + "------------INS literal:number@@0 @TO@ expr@@i1 = 0 @AT@ 298 @LENGTH@ 1\n"); } @@ -303,7 +294,7 @@ public class TestRealCases { "---------UPD expr@@a < ( r1 % 10 ) @TO@ a < r1 @AT@ 193 @LENGTH@ 15\n" + "------------DEL operator@@( @AT@ 195 @LENGTH@ 1\n" + "------------DEL operator@@% @AT@ 198 @LENGTH@ 1\n" + - "------------DEL literal@@10 @AT@ 199 @LENGTH@ 2\n" + + "------------DEL literal:number@@10 @AT@ 199 @LENGTH@ 2\n" + "------------DEL operator@@) @AT@ 201 @LENGTH@ 1\n"); } @@ -333,7 +324,7 @@ public class TestRealCases { "------UPD condition@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" + "---------UPD expr@@i < n @TO@ i < n - 1 @AT@ 163 @LENGTH@ 5\n" + "------------INS operator@@- @TO@ expr@@i < n @AT@ 166 @LENGTH@ 1\n" + - "------------INS literal@@1 @TO@ expr@@i < n @AT@ 167 @LENGTH@ 1\n" + + "------------INS literal:number@@1 @TO@ expr@@i < n @AT@ 167 @LENGTH@ 1\n" + "---UPD block@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 173 @LENGTH@ 56\n" + "------UPD if@@if a ][i + 1 == a ][i d = d + 1 else f = f + 1 i = i + 2 @TO@ if a ][i + 1 == a ][i d = d + 1 else f = f + 1 @AT@ 179 @LENGTH@ 56\n" + "---------UPD else@@else f = f + 1 i = i + 2 @TO@ else f = f + 1 @AT@ 218 @LENGTH@ 24\n" + @@ -344,7 +335,7 @@ public class TestRealCases { "---------------------DEL operator@@= @AT@ 234 @LENGTH@ 1\n" + "---------------------DEL name@@i @AT@ 235 @LENGTH@ 1\n" + "---------------------DEL operator@@+ @AT@ 236 @LENGTH@ 1\n" + - "---------------------DEL literal@@2 @AT@ 237 @LENGTH@ 1\n"); + "---------------------DEL literal:number@@2 @AT@ 237 @LENGTH@ 1\n"); } @Test @@ -368,10 +359,10 @@ public class TestRealCases { "------------------------------------UPD argument_list@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" n m n m @AT@ 1324 @LENGTH@ 34\n" + "---------------------------------------UPD argument@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + "------------------------------------------UPD expr@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + - "---------------------------------------------UPD literal@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + + "---------------------------------------------UPD literal:string@@\"%d %d\\n0 0 \\n%d \\n0 %d\\n\" @TO@ \"%d %d\\n0 0 \\n%d 0\\n0 %d\\n\" @AT@ 1325 @LENGTH@ 26\n" + "---INS return@@return 0 @TO@ block@@int i j n m x i_temp 1 scanf \"%d %d\" & n & m if n == 0 printf \"0 1\\n\" printf \"0 %d\\n\" m printf \"0 0\\n\" printf \"0 %d\\n\" ( m - 1 ) return 0 elseif if m == 0 printf \"1 0\\n\" printf \"%d 0\\n\" n printf \"0 0\\n\" printf \"%d 0\\n\" ( n - 1 ) return 0 elseif if ( m == n ) && ( n == 1 ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" ( m ) return 0 elseif if m == n if m + m * 1.41f > ( 2 * sqrt double m * m + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m printf \"0 0\\n\" printf \"%d 0\\n\" n printf \"0 %d\\n\" n else printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 elseif if n < m if m + sqrt ( double ) n * n + m * m < ( 2 * sqrt double n - 1 * ( n - 1 ) + ( m ) * ( m ) ) printf \"%d %d\\n\" n - 1 m printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"1 0\\n\" return 0 else printf \"%d %d\\n0 0 \\n0 %d\\n%d 0\\n\" n m m n return 0 elseif if n > m if n + sqrt ( double ) n * n + m * m < ( 2 * sqrt ( double ) n * n + ( m - 1 ) * ( m - 1 ) ) printf \"%d %d\\n\" n m - 1 printf \"0 0\\n\" printf \"%d %d\\n\" n m printf \"0 1\\n\" return 0 else printf \"%d %d\\n0 0 \\n%d \\n0 %d\\n\" n m n m return 0 @AT@ 1385 @LENGTH@ 8\n" + "------INS expr@@0 @TO@ return@@return 0 @AT@ 1392 @LENGTH@ 1\n" + - "---------INS literal@@0 @TO@ expr@@0 @AT@ 1392 @LENGTH@ 1\n"); + "---------INS literal:number@@0 @TO@ expr@@0 @AT@ 1392 @LENGTH@ 1\n"); } @@ -387,7 +378,7 @@ public class TestRealCases { "---------UPD argument_list@@ans 86401 - dp ][k @TO@ ans 86400 - dp ][k @AT@ 386 @LENGTH@ 18\n" + "------------UPD argument@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" + "---------------UPD expr@@86401 - dp ][k @TO@ 86400 - dp ][k @AT@ 393 @LENGTH@ 14\n" + - "------------------UPD literal@@86401 @TO@ 86400 @AT@ 393 @LENGTH@ 5\n"); + "------------------UPD literal:number@@86401 @TO@ 86400 @AT@ 393 @LENGTH@ 5\n"); } @Test @@ -402,7 +393,7 @@ public class TestRealCases { "---------UPD argument_list@@negative / 2 @TO@ negative / 2.0 @AT@ 303 @LENGTH@ 12\n" + "------------UPD argument@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" + "---------------UPD expr@@negative / 2 @TO@ negative / 2.0 @AT@ 304 @LENGTH@ 12\n" + - "------------------UPD literal@@2 @TO@ 2.0 @AT@ 313 @LENGTH@ 1\n"); + "------------------UPD literal:number@@2 @TO@ 2.0 @AT@ 313 @LENGTH@ 1\n"); } @@ -468,7 +459,7 @@ public class TestRealCases { "---------------INS expr@@[i @TO@ index@@][i @AT@ 522 @LENGTH@ 2\n" + "------------------INS name@@[i @TO@ expr@@[i @AT@ 522 @LENGTH@ 2\n" + "---------INS operator@@!= @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 524 @LENGTH@ 2\n" + - "---------INS literal@@0 @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 526 @LENGTH@ 1\n"); + "---------INS literal:number@@0 @TO@ expr@@a ][i >= a ][k && i <= n @AT@ 526 @LENGTH@ 1\n"); } @@ -562,12 +553,12 @@ public class TestRealCases { "---------DEL expr@@i = 0 @AT@ 454 @LENGTH@ 5\n" + "------------DEL name@@i @AT@ 454 @LENGTH@ 1\n" + "------------DEL operator@@= @AT@ 455 @LENGTH@ 1\n" + - "------------DEL literal@@0 @AT@ 456 @LENGTH@ 1\n" + + "------------DEL literal:number@@0 @AT@ 456 @LENGTH@ 1\n" + "------DEL condition@@i < 10 @AT@ 458 @LENGTH@ 6\n" + "---------DEL expr@@i < 10 @AT@ 458 @LENGTH@ 6\n" + "------------DEL name@@i @AT@ 458 @LENGTH@ 1\n" + "------------DEL operator@@< @AT@ 459 @LENGTH@ 1\n" + - "------------DEL literal@@10 @AT@ 460 @LENGTH@ 2\n" + + "------------DEL literal:number@@10 @AT@ 460 @LENGTH@ 2\n" + "------DEL incr@@i ++ @AT@ 463 @LENGTH@ 4\n" + "---------DEL expr@@i ++ @AT@ 463 @LENGTH@ 4\n" + "------------DEL name@@i @AT@ 463 @LENGTH@ 1\n" + @@ -580,7 +571,7 @@ public class TestRealCases { "---------------DEL argument_list@@\"%d %d\\n\" num ][i tnum ][i @AT@ 483 @LENGTH@ 26\n" + "------------------DEL argument@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + "---------------------DEL expr@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + - "------------------------DEL literal@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + + "------------------------DEL literal:string@@\"%d %d\\n\" @AT@ 484 @LENGTH@ 9\n" + "------------------DEL argument@@num ][i @AT@ 494 @LENGTH@ 7\n" + "---------------------DEL expr@@num ][i @AT@ 494 @LENGTH@ 7\n" + "------------------------DEL name@@num ][i @AT@ 494 @LENGTH@ 7\n" + @@ -660,7 +651,7 @@ public class TestRealCases { "------INS expr@@max == 0 @TO@ condition@@max == 0 @AT@ 693 @LENGTH@ 8\n" + "---------INS name@@max @TO@ expr@@max == 0 @AT@ 693 @LENGTH@ 3\n" + "---------INS operator@@== @TO@ expr@@max == 0 @AT@ 696 @LENGTH@ 2\n" + - "---------INS literal@@0 @TO@ expr@@max == 0 @AT@ 698 @LENGTH@ 1\n" + + "---------INS literal:number@@0 @TO@ expr@@max == 0 @AT@ 698 @LENGTH@ 1\n" + "---INS then@@printf \"0\\n\" continue; @TO@ if@@if max == 0 printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" + "------INS block@@printf \"0\\n\" continue; @TO@ then@@printf \"0\\n\" continue; @AT@ 702 @LENGTH@ 22\n" + "---------INS expr_stmt@@printf \"0\\n\" @TO@ block@@printf \"0\\n\" continue; @AT@ 703 @LENGTH@ 12\n" + @@ -670,7 +661,7 @@ public class TestRealCases { "------------------INS argument_list@@\"0\\n\" @TO@ call@@printf \"0\\n\" @AT@ 709 @LENGTH@ 5\n" + "---------------------INS argument@@\"0\\n\" @TO@ argument_list@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + "------------------------INS expr@@\"0\\n\" @TO@ argument@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + - "---------------------------INS literal@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + + "---------------------------INS literal:string@@\"0\\n\" @TO@ expr@@\"0\\n\" @AT@ 710 @LENGTH@ 5\n" + "---------INS continue@@continue; @TO@ block@@printf \"0\\n\" continue; @AT@ 717 @LENGTH@ 9\n"); } @@ -723,6 +714,46 @@ public class TestRealCases { "------INS operator@@= @TO@ expr@@b ][x ][y = 1 @AT@ 262 @LENGTH@ 1\n"); } + @Test + public void test_254_B_2751488_2751528() throws IOException { + + List hierarchicalActionSets = getHierarchicalActionSets("254-B-2751488-2751528.c"); + Assert.assertEquals(hierarchicalActionSets.size(), 1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD if@@if d <= 0 * m = * m - 1 * d = days ][* m @TO@ if * d <= 0 * m = * m - 1 * d = days ][* m @AT@ 197 @LENGTH@ 40\n" + + "---UPD condition@@d <= 0 @TO@ * d <= 0 @AT@ 197 @LENGTH@ 6\n" + + "------UPD expr@@d <= 0 @TO@ * d <= 0 @AT@ 198 @LENGTH@ 6\n" + + "---------INS operator@@* @TO@ expr@@d <= 0 @AT@ 198 @LENGTH@ 1\n"); + } + + //codeflaws_430-C-6594918-6595299.c + @Test + public void test_430_C_6594918_6595299() throws IOException { + + List hierarchicalActionSets = getHierarchicalActionSets("430-C-6594918-6595299.c"); + Assert.assertEquals(hierarchicalActionSets.size(), 1); + Assert.assertEquals(hierarchicalActionSets.get(0).toString(),"UPD block@@stk ][front ++ = nn -> v v ][nn -> v -> c = 0 if cc == 1 val ][nn -> v ++ @TO@ stk ][front ++ = nn -> v v ][nn -> v -> c = 0 val ][nn -> v += val ][stk ][rear @AT@ 1733 @LENGTH@ 73\n" + + "---DEL if@@if cc == 1 val ][nn -> v ++ @AT@ 1832 @LENGTH@ 27\n" + + "------DEL condition@@cc == 1 @AT@ 1832 @LENGTH@ 7\n" + + "---------DEL expr@@cc == 1 @AT@ 1833 @LENGTH@ 7\n" + + "------------DEL name@@cc @AT@ 1833 @LENGTH@ 2\n" + + "------------DEL operator@@== @AT@ 1835 @LENGTH@ 2\n" + + "------------DEL literal:number@@1 @AT@ 1837 @LENGTH@ 1\n" + + "------DEL then@@val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" + + "---------DEL block@@val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" + + "---MOV expr_stmt@@val ][nn -> v ++ @TO@ block@@stk ][front ++ = nn -> v v ][nn -> v -> c = 0 if cc == 1 val ][nn -> v ++ @AT@ 1864 @LENGTH@ 16\n" + + "------UPD expr_stmt@@val ][nn -> v ++ @TO@ val ][nn -> v += val ][stk ][rear @AT@ 1864 @LENGTH@ 16\n" + + "---------UPD expr@@val ][nn -> v ++ @TO@ val ][nn -> v += val ][stk ][rear @AT@ 1864 @LENGTH@ 16\n" + + "------------INS name@@val ][stk ][rear @TO@ expr@@val ][nn -> v ++ @AT@ 1842 @LENGTH@ 16\n" + + "---------------INS name@@val @TO@ name@@val ][stk ][rear @AT@ 1842 @LENGTH@ 3\n" + + "---------------INS index@@][stk ][rear @TO@ name@@val ][stk ][rear @AT@ 1846 @LENGTH@ 12\n" + + "------------------INS expr@@[stk ][rear @TO@ index@@][stk ][rear @AT@ 1846 @LENGTH@ 11\n" + + "---------------------INS name@@[stk ][rear @TO@ expr@@[stk ][rear @AT@ 1846 @LENGTH@ 11\n" + + "------------------------INS name@@[stk @TO@ name@@[stk ][rear @AT@ 1846 @LENGTH@ 4\n" + + "------------------------INS index@@][rear @TO@ name@@[stk ][rear @AT@ 1850 @LENGTH@ 6\n" + + "---------------------------INS expr@@[rear @TO@ index@@][rear @AT@ 1850 @LENGTH@ 5\n" + + "------------------------------INS name@@[rear @TO@ expr@@[rear @AT@ 1850 @LENGTH@ 5\n" + + "------------UPD operator@@++ @TO@ += @AT@ 1874 @LENGTH@ 2\n"); + } public List getHierarchicalActionSets(String s) throws IOException { Properties appProps = new Properties(); appProps.load(new FileInputStream("src/main/resource/app.properties")); diff --git a/src/main/resource/MF0012.local.app.properties b/src/main/resource/MF0012.local.app.properties index d424802..b0dcf48 100644 --- a/src/main/resource/MF0012.local.app.properties +++ b/src/main/resource/MF0012.local.app.properties @@ -4,7 +4,8 @@ numOfWorkers = 14 hunkLimit = 10 patchSize = 50 -projectList = libtiff,php-src,cpython,wireshark,gzip,gmp,lighttpd1.4,lighttpd2 +#projectList = libtiff,php-src,cpython,wireshark,gzip,gmp,lighttpd1.4,lighttpd2 +projectList = codeflaws #inputPath = /Users/anilkoyuncu/projects/gumInputLinux #inputPath = /Users/anil.koyuncu/projects/fixminer/fixminer-data/gumInputLinux diff --git a/src/main/resource/testFiles/codeflaws/prevFiles/prev_254-B-2751488-2751528.c b/src/main/resource/testFiles/codeflaws/prevFiles/prev_254-B-2751488-2751528.c new file mode 100644 index 0000000..c527dd0 --- /dev/null +++ b/src/main/resource/testFiles/codeflaws/prevFiles/prev_254-B-2751488-2751528.c @@ -0,0 +1,45 @@ +#include +int days[25]={ 0,31,29,31,30,31,30,31,31,30,31,30,31, + 31,28,31,30,31,30,31,31,30,31,30,31}; +int map[25][32]={0}; +void minus(int* m,int* d){ + *d = *d -1; + if(d<=0){ + *m = *m - 1; + *d = days[*m]; + } +} +int main(int argc,char** argv){ + int n; + int m=0,d=0,p=0,t=0; + int i,j,k; + int max=-1; + //freopen("input.txt", "r", stdin); + //freopen("output.txt", "w", stdout); + scanf("%d",&n); + for(i=0;i +#include +#include + +int cur[100010],tar[100010],val[100010],op,stk[100010],temp[100010]; +struct node{ + int v,c; + struct node *next; +}; +void init(int n,struct node *p[n],struct node *v[n]){ + int i=1; + while (iv=i; + v[i]->next=NULL; + v[i]->c=1; + p[i]=NULL; + i++; + } +} + +void create(int n,struct node *p[n],struct node *v[n],int x,int y){ + struct node *a=malloc(sizeof(struct node)),*b=malloc(sizeof(struct node)); + a->v=y; + b->v=x; + a->c=1;b->c=1; + a->next=p[x]; + b->next=p[y]; + p[x]=a; + p[y]=b; +} +int main(int argc, char *argv[]){ + int n,i=1; + scanf("%d",&n); + struct node *p[n+1],*v[n+1]; + init(n+1, p, v); + while (ic=0; + while (rearv]->c==1){ + stk[front++]=n->v; + v[n->v]->c=0; + } + struct node *nn=p[n->v]; + while (nn!=NULL) { + if(v[nn->v]->c==1){ + stk[front++]=nn->v; + v[nn->v]->c=0; + if(cc==1) + val[nn->v]++; + //printf("%d %d %d \n",stk[rear],nn->v,val[nn->v]); + } + nn=nn->next; + } + n=n->next; + } + rear++; + } + printf("%d\n",op); + i=0; + while (i +int days[25]={ 0,31,29,31,30,31,30,31,31,30,31,30,31, + 31,28,31,30,31,30,31,31,30,31,30,31}; +int map[25][32]={0}; +void minus(int* m,int* d){ + *d = *d -1; + if(*d<=0){ + *m = *m - 1; + *d = days[*m]; + } +} +int main(int argc,char** argv){ + int n; + int m=0,d=0,p=0,t=0; + int i,j,k; + int max=-1; + //freopen("input.txt", "r", stdin); + //freopen("output.txt", "w", stdout); + scanf("%d",&n); + for(i=0;i +#include +#include + +int cur[100010],tar[100010],val[100010],op,stk[100010],temp[100010]; +struct node{ + int v,c; + struct node *next; +}; +void init(int n,struct node *p[n],struct node *v[n]){ + int i=1; + while (iv=i; + v[i]->next=NULL; + v[i]->c=1; + p[i]=NULL; + i++; + } +} + +void create(int n,struct node *p[n],struct node *v[n],int x,int y){ + struct node *a=malloc(sizeof(struct node)),*b=malloc(sizeof(struct node)); + a->v=y; + b->v=x; + a->c=1;b->c=1; + a->next=p[x]; + b->next=p[y]; + p[x]=a; + p[y]=b; +} +int main(int argc, char *argv[]){ + int n,i=1; + scanf("%d",&n); + struct node *p[n+1],*v[n+1]; + init(n+1, p, v); + while (ic=0; + while (rearv]->c==1){ + stk[front++]=n->v; + v[n->v]->c=0; + } + struct node *nn=p[n->v]; + while (nn!=NULL) { + if(v[nn->v]->c==1){ + stk[front++]=nn->v; + v[nn->v]->c=0; + val[nn->v]+=val[stk[rear]]; + //printf("%d %d %d \n",stk[rear],nn->v,val[nn->v]); + } + nn=nn->next; + } + n=n->next; + } + rear++; + } + printf("%d\n",op); + i=0; + while (i +#endif + +#include + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifndef SIG_ERR +#define SIG_ERR ((PyOS_sighandler_t)(-1)) +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#include +#endif + +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + + +/* + NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS + + When threads are supported, we want the following semantics: + + - only the main thread can set a signal handler + - any thread can get a signal handler + - signals are only delivered to the main thread + + I.e. we don't support "synchronous signals" like SIGFPE (catching + this doesn't make much sense in Python anyway) nor do we support + signals as a means of inter-thread communication, since not all + thread implementations support that (at least our thread library + doesn't). + + We still have the problem that in some implementations signals + generated by the keyboard (e.g. SIGINT) are delivered to all + threads (e.g. SGI), while in others (e.g. Solaris) such signals are + delivered to one random thread (an intermediate possibility would + be to deliver it to the main thread -- POSIX?). For now, we have + a working implementation that works in all three cases -- the + handler ignores signals if getpid() isn't the same as in the main + thread. XXX This is a hack. + + GNU pth is a user-space threading library, and as such, all threads + run within the same process. In this case, if the currently running + thread is not the main_thread, send the signal to the main_thread. +*/ + +#ifdef WITH_THREAD +#include /* For pid_t */ +#include "pythread.h" +static long main_thread; +static pid_t main_pid; +#endif + +static struct { + int tripped; + PyObject *func; +} Handlers[NSIG]; + +static sig_atomic_t wakeup_fd = -1; + +/* Speed up sigcheck() when none tripped */ +static volatile sig_atomic_t is_tripped = 0; + +static PyObject *DefaultHandler; +static PyObject *IgnoreHandler; +static PyObject *IntHandler; + +/* On Solaris 8, gcc will produce a warning that the function + declaration is not a prototype. This is caused by the definition of + SIG_DFL as (void (*)())0; the correct declaration would have been + (void (*)(int))0. */ + +static PyOS_sighandler_t old_siginthandler = SIG_DFL; + +#ifdef HAVE_GETITIMER +static PyObject *ItimerError; + +/* auxiliary functions for setitimer/getitimer */ +static void +timeval_from_double(double d, struct timeval *tv) +{ + tv->tv_sec = floor(d); + tv->tv_usec = fmod(d, 1.0) * 1000000.0; +} + +static inline double +double_from_timeval(struct timeval *tv) +{ + return tv->tv_sec + (double)(tv->tv_usec / 1000000.0); +} + +static PyObject * +itimer_retval(struct itimerval *iv) +{ + PyObject *r, *v; + + r = PyTuple_New(2); + if (r == NULL) + return NULL; + + if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) { + Py_DECREF(r); + return NULL; + } + + PyTuple_SET_ITEM(r, 0, v); + + if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) { + Py_DECREF(r); + return NULL; + } + + PyTuple_SET_ITEM(r, 1, v); + + return r; +} +#endif + +static PyObject * +signal_default_int_handler(PyObject *self, PyObject *args) +{ + PyErr_SetNone(PyExc_KeyboardInterrupt); + return NULL; +} + +PyDoc_STRVAR(default_int_handler_doc, +"default_int_handler(...)\n\ +\n\ +The default handler for SIGINT installed by Python.\n\ +It raises KeyboardInterrupt."); + + +static int +checksignals_witharg(void * unused) +{ + return PyErr_CheckSignals(); +} + +static void +signal_handler(int sig_num) +{ +#ifdef WITH_THREAD +#ifdef WITH_PTH + if (PyThread_get_thread_ident() != main_thread) { + pth_raise(*(pth_t *) main_thread, sig_num); + return; + } +#endif + /* See NOTES section above */ + if (getpid() == main_pid) { +#endif + Handlers[sig_num].tripped = 1; + /* Set is_tripped after setting .tripped, as it gets + cleared in PyErr_CheckSignals() before .tripped. */ + is_tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); + if (wakeup_fd != -1) + write(wakeup_fd, "\0", 1); +#ifdef WITH_THREAD + } +#endif +#ifdef SIGCHLD + if (sig_num == SIGCHLD) { + /* To avoid infinite recursion, this signal remains + reset until explicit re-instated. + Don't clear the 'func' field as it is our pointer + to the Python handler... */ + return; + } +#endif + PyOS_setsig(sig_num, signal_handler); +} + + +#ifdef HAVE_ALARM +static PyObject * +signal_alarm(PyObject *self, PyObject *args) +{ + int t; + if (!PyArg_ParseTuple(args, "i:alarm", &t)) + return NULL; + /* alarm() returns the number of seconds remaining */ + return PyLong_FromLong((long)alarm(t)); +} + +PyDoc_STRVAR(alarm_doc, +"alarm(seconds)\n\ +\n\ +Arrange for SIGALRM to arrive after the given number of seconds."); +#endif + +#ifdef HAVE_PAUSE +static PyObject * +signal_pause(PyObject *self) +{ + Py_BEGIN_ALLOW_THREADS + (void)pause(); + Py_END_ALLOW_THREADS + /* make sure that any exceptions that got raised are propagated + * back into Python + */ + if (PyErr_CheckSignals()) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} +PyDoc_STRVAR(pause_doc, +"pause()\n\ +\n\ +Wait until a signal arrives."); + +#endif + + +static PyObject * +signal_signal(PyObject *self, PyObject *args) +{ + PyObject *obj; + int sig_num; + PyObject *old_handler; + void (*func)(int); + if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) + return NULL; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) { + PyErr_SetString(PyExc_ValueError, + "signal only works in main thread"); + return NULL; + } +#endif + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (obj == IgnoreHandler) + func = SIG_IGN; + else if (obj == DefaultHandler) + func = SIG_DFL; + else if (!PyCallable_Check(obj)) { + PyErr_SetString(PyExc_TypeError, +"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"); + return NULL; + } + else + func = signal_handler; + if (PyOS_setsig(sig_num, func) == SIG_ERR) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + old_handler = Handlers[sig_num].func; + Handlers[sig_num].tripped = 0; + Py_INCREF(obj); + Handlers[sig_num].func = obj; + return old_handler; +} + +PyDoc_STRVAR(signal_doc, +"signal(sig, action) -> action\n\ +\n\ +Set the action for the given signal. The action can be SIG_DFL,\n\ +SIG_IGN, or a callable Python object. The previous action is\n\ +returned. See getsignal() for possible return values.\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + + +static PyObject * +signal_getsignal(PyObject *self, PyObject *args) +{ + int sig_num; + PyObject *old_handler; + if (!PyArg_ParseTuple(args, "i:getsignal", &sig_num)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + old_handler = Handlers[sig_num].func; + Py_INCREF(old_handler); + return old_handler; +} + +PyDoc_STRVAR(getsignal_doc, +"getsignal(sig) -> action\n\ +\n\ +Return the current action for the given signal. The return value can be:\n\ +SIG_IGN -- if the signal is being ignored\n\ +SIG_DFL -- if the default action for the signal is in effect\n\ +None -- if an unknown handler is in effect\n\ +anything else -- the callable Python object used as a handler"); + +#ifdef HAVE_SIGINTERRUPT +PyDoc_STRVAR(siginterrupt_doc, +"siginterrupt(sig, flag) -> None\n\ +change system call restart behaviour: if flag is False, system calls\n\ +will be restarted when interrupted by signal sig, else system calls\n\ +will be interrupted."); + +static PyObject * +signal_siginterrupt(PyObject *self, PyObject *args) +{ + int sig_num; + int flag; + + if (!PyArg_ParseTuple(args, "ii:siginterrupt", &sig_num, &flag)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (siginterrupt(sig_num, flag)<0) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +static PyObject * +signal_set_wakeup_fd(PyObject *self, PyObject *args) +{ + struct stat buf; + int fd, old_fd; + if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd)) + return NULL; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) { + PyErr_SetString(PyExc_ValueError, + "set_wakeup_fd only works in main thread"); + return NULL; + } +#endif + if (fd != -1 && fstat(fd, &buf) != 0) { + PyErr_SetString(PyExc_ValueError, "invalid fd"); + return NULL; + } + old_fd = wakeup_fd; + wakeup_fd = fd; + return PyLong_FromLong(old_fd); +} + +PyDoc_STRVAR(set_wakeup_fd_doc, +"set_wakeup_fd(fd) -> fd\n\ +\n\ +Sets the fd to be written to (with '\\0') when a signal\n\ +comes in. A library can use this to wakeup select or poll.\n\ +The previous fd is returned.\n\ +\n\ +The fd must be non-blocking."); + +/* C API for the same, without all the error checking */ +int +PySignal_SetWakeupFd(int fd) +{ + int old_fd = wakeup_fd; + if (fd < 0) + fd = -1; + wakeup_fd = fd; + return old_fd; +} + + +#ifdef HAVE_SETITIMER +static PyObject * +signal_setitimer(PyObject *self, PyObject *args) +{ + double first; + double interval = 0; + int which; + struct itimerval new, old; + + if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval)) + return NULL; + + timeval_from_double(first, &new.it_value); + timeval_from_double(interval, &new.it_interval); + /* Let OS check "which" value */ + if (setitimer(which, &new, &old) != 0) { + PyErr_SetFromErrno(ItimerError); + return NULL; + } + + return itimer_retval(&old); +} + +PyDoc_STRVAR(setitimer_doc, +"setitimer(which, seconds[, interval])\n\ +\n\ +Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\ +or ITIMER_PROF) to fire after value seconds and after\n\ +that every interval seconds.\n\ +The itimer can be cleared by setting seconds to zero.\n\ +\n\ +Returns old values as a tuple: (delay, interval)."); +#endif + + +#ifdef HAVE_GETITIMER +static PyObject * +signal_getitimer(PyObject *self, PyObject *args) +{ + int which; + struct itimerval old; + + if (!PyArg_ParseTuple(args, "i:getitimer", &which)) + return NULL; + + if (getitimer(which, &old) != 0) { + PyErr_SetFromErrno(ItimerError); + return NULL; + } + + return itimer_retval(&old); +} + +PyDoc_STRVAR(getitimer_doc, +"getitimer(which)\n\ +\n\ +Returns current value of given itimer."); +#endif + + +/* List of functions defined in the module */ +static PyMethodDef signal_methods[] = { +#ifdef HAVE_ALARM + {"alarm", signal_alarm, METH_VARARGS, alarm_doc}, +#endif +#ifdef HAVE_SETITIMER + {"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc}, +#endif +#ifdef HAVE_GETITIMER + {"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc}, +#endif + {"signal", signal_signal, METH_VARARGS, signal_doc}, + {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, + {"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc}, +#ifdef HAVE_SIGINTERRUPT + {"siginterrupt", signal_siginterrupt, METH_VARARGS, siginterrupt_doc}, +#endif +#ifdef HAVE_PAUSE + {"pause", (PyCFunction)signal_pause, + METH_NOARGS,pause_doc}, +#endif + {"default_int_handler", signal_default_int_handler, + METH_VARARGS, default_int_handler_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module provides mechanisms to use signal handlers in Python.\n\ +\n\ +Functions:\n\ +\n\ +alarm() -- cause SIGALRM after a specified time [Unix only]\n\ +setitimer() -- cause a signal (described below) after a specified\n\ + float time and the timer may restart then [Unix only]\n\ +getitimer() -- get current value of timer [Unix only]\n\ +signal() -- set the action for a given signal\n\ +getsignal() -- get the signal action for a given signal\n\ +pause() -- wait until a signal arrives [Unix only]\n\ +default_int_handler() -- default SIGINT handler\n\ +\n\ +signal constants:\n\ +SIG_DFL -- used to refer to the system default handler\n\ +SIG_IGN -- used to ignore the signal\n\ +NSIG -- number of defined signals\n\ +SIGINT, SIGTERM, etc. -- signal numbers\n\ +\n\ +itimer constants:\n\ +ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\ + expiration\n\ +ITIMER_VIRTUAL -- decrements only when the process is executing,\n\ + and delivers SIGVTALRM upon expiration\n\ +ITIMER_PROF -- decrements both when the process is executing and\n\ + when the system is executing on behalf of the process.\n\ + Coupled with ITIMER_VIRTUAL, this timer is usually\n\ + used to profile the time spent by the application\n\ + in user and kernel space. SIGPROF is delivered upon\n\ + expiration.\n\ +\n\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + +static struct PyModuleDef signalmodule = { + PyModuleDef_HEAD_INIT, + "signal", + module_doc, + -1, + signal_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_signal(void) +{ + PyObject *m, *d, *x; + int i; + +#ifdef WITH_THREAD + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); +#endif + + /* Create the module and add the functions */ + m = PyModule_Create(&signalmodule); + if (m == NULL) + return NULL; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL); + if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0) + goto finally; + + x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN); + if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0) + goto finally; + + x = PyLong_FromLong((long)NSIG); + if (!x || PyDict_SetItemString(d, "NSIG", x) < 0) + goto finally; + Py_DECREF(x); + + x = IntHandler = PyDict_GetItemString(d, "default_int_handler"); + if (!x) + goto finally; + Py_INCREF(IntHandler); + + Handlers[0].tripped = 0; + for (i = 1; i < NSIG; i++) { + void (*t)(int); + t = PyOS_getsig(i); + Handlers[i].tripped = 0; + if (t == SIG_DFL) + Handlers[i].func = DefaultHandler; + else if (t == SIG_IGN) + Handlers[i].func = IgnoreHandler; + else + Handlers[i].func = Py_None; /* None of our business */ + Py_INCREF(Handlers[i].func); + } + if (Handlers[SIGINT].func == DefaultHandler) { + /* Install default int handler */ + Py_INCREF(IntHandler); + Py_DECREF(Handlers[SIGINT].func); + Handlers[SIGINT].func = IntHandler; + old_siginthandler = PyOS_setsig(SIGINT, signal_handler); + } + +#ifdef SIGHUP + x = PyLong_FromLong(SIGHUP); + PyDict_SetItemString(d, "SIGHUP", x); + Py_XDECREF(x); +#endif +#ifdef SIGINT + x = PyLong_FromLong(SIGINT); + PyDict_SetItemString(d, "SIGINT", x); + Py_XDECREF(x); +#endif +#ifdef SIGBREAK + x = PyLong_FromLong(SIGBREAK); + PyDict_SetItemString(d, "SIGBREAK", x); + Py_XDECREF(x); +#endif +#ifdef SIGQUIT + x = PyLong_FromLong(SIGQUIT); + PyDict_SetItemString(d, "SIGQUIT", x); + Py_XDECREF(x); +#endif +#ifdef SIGILL + x = PyLong_FromLong(SIGILL); + PyDict_SetItemString(d, "SIGILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGTRAP + x = PyLong_FromLong(SIGTRAP); + PyDict_SetItemString(d, "SIGTRAP", x); + Py_XDECREF(x); +#endif +#ifdef SIGIOT + x = PyLong_FromLong(SIGIOT); + PyDict_SetItemString(d, "SIGIOT", x); + Py_XDECREF(x); +#endif +#ifdef SIGABRT + x = PyLong_FromLong(SIGABRT); + PyDict_SetItemString(d, "SIGABRT", x); + Py_XDECREF(x); +#endif +#ifdef SIGEMT + x = PyLong_FromLong(SIGEMT); + PyDict_SetItemString(d, "SIGEMT", x); + Py_XDECREF(x); +#endif +#ifdef SIGFPE + x = PyLong_FromLong(SIGFPE); + PyDict_SetItemString(d, "SIGFPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGKILL + x = PyLong_FromLong(SIGKILL); + PyDict_SetItemString(d, "SIGKILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGBUS + x = PyLong_FromLong(SIGBUS); + PyDict_SetItemString(d, "SIGBUS", x); + Py_XDECREF(x); +#endif +#ifdef SIGSEGV + x = PyLong_FromLong(SIGSEGV); + PyDict_SetItemString(d, "SIGSEGV", x); + Py_XDECREF(x); +#endif +#ifdef SIGSYS + x = PyLong_FromLong(SIGSYS); + PyDict_SetItemString(d, "SIGSYS", x); + Py_XDECREF(x); +#endif +#ifdef SIGPIPE + x = PyLong_FromLong(SIGPIPE); + PyDict_SetItemString(d, "SIGPIPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGALRM + x = PyLong_FromLong(SIGALRM); + PyDict_SetItemString(d, "SIGALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGTERM + x = PyLong_FromLong(SIGTERM); + PyDict_SetItemString(d, "SIGTERM", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR1 + x = PyLong_FromLong(SIGUSR1); + PyDict_SetItemString(d, "SIGUSR1", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR2 + x = PyLong_FromLong(SIGUSR2); + PyDict_SetItemString(d, "SIGUSR2", x); + Py_XDECREF(x); +#endif +#ifdef SIGCLD + x = PyLong_FromLong(SIGCLD); + PyDict_SetItemString(d, "SIGCLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGCHLD + x = PyLong_FromLong(SIGCHLD); + PyDict_SetItemString(d, "SIGCHLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGPWR + x = PyLong_FromLong(SIGPWR); + PyDict_SetItemString(d, "SIGPWR", x); + Py_XDECREF(x); +#endif +#ifdef SIGIO + x = PyLong_FromLong(SIGIO); + PyDict_SetItemString(d, "SIGIO", x); + Py_XDECREF(x); +#endif +#ifdef SIGURG + x = PyLong_FromLong(SIGURG); + PyDict_SetItemString(d, "SIGURG", x); + Py_XDECREF(x); +#endif +#ifdef SIGWINCH + x = PyLong_FromLong(SIGWINCH); + PyDict_SetItemString(d, "SIGWINCH", x); + Py_XDECREF(x); +#endif +#ifdef SIGPOLL + x = PyLong_FromLong(SIGPOLL); + PyDict_SetItemString(d, "SIGPOLL", x); + Py_XDECREF(x); +#endif +#ifdef SIGSTOP + x = PyLong_FromLong(SIGSTOP); + PyDict_SetItemString(d, "SIGSTOP", x); + Py_XDECREF(x); +#endif +#ifdef SIGTSTP + x = PyLong_FromLong(SIGTSTP); + PyDict_SetItemString(d, "SIGTSTP", x); + Py_XDECREF(x); +#endif +#ifdef SIGCONT + x = PyLong_FromLong(SIGCONT); + PyDict_SetItemString(d, "SIGCONT", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTIN + x = PyLong_FromLong(SIGTTIN); + PyDict_SetItemString(d, "SIGTTIN", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTOU + x = PyLong_FromLong(SIGTTOU); + PyDict_SetItemString(d, "SIGTTOU", x); + Py_XDECREF(x); +#endif +#ifdef SIGVTALRM + x = PyLong_FromLong(SIGVTALRM); + PyDict_SetItemString(d, "SIGVTALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGPROF + x = PyLong_FromLong(SIGPROF); + PyDict_SetItemString(d, "SIGPROF", x); + Py_XDECREF(x); +#endif +#ifdef SIGXCPU + x = PyLong_FromLong(SIGXCPU); + PyDict_SetItemString(d, "SIGXCPU", x); + Py_XDECREF(x); +#endif +#ifdef SIGXFSZ + x = PyLong_FromLong(SIGXFSZ); + PyDict_SetItemString(d, "SIGXFSZ", x); + Py_XDECREF(x); +#endif +#ifdef SIGRTMIN + x = PyLong_FromLong(SIGRTMIN); + PyDict_SetItemString(d, "SIGRTMIN", x); + Py_XDECREF(x); +#endif +#ifdef SIGRTMAX + x = PyLong_FromLong(SIGRTMAX); + PyDict_SetItemString(d, "SIGRTMAX", x); + Py_XDECREF(x); +#endif +#ifdef SIGINFO + x = PyLong_FromLong(SIGINFO); + PyDict_SetItemString(d, "SIGINFO", x); + Py_XDECREF(x); +#endif + +#ifdef ITIMER_REAL + x = PyLong_FromLong(ITIMER_REAL); + PyDict_SetItemString(d, "ITIMER_REAL", x); + Py_DECREF(x); +#endif +#ifdef ITIMER_VIRTUAL + x = PyLong_FromLong(ITIMER_VIRTUAL); + PyDict_SetItemString(d, "ITIMER_VIRTUAL", x); + Py_DECREF(x); +#endif +#ifdef ITIMER_PROF + x = PyLong_FromLong(ITIMER_PROF); + PyDict_SetItemString(d, "ITIMER_PROF", x); + Py_DECREF(x); +#endif + +#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER) + ItimerError = PyErr_NewException("signal.ItimerError", + PyExc_IOError, NULL); + if (ItimerError != NULL) + PyDict_SetItemString(d, "ItimerError", ItimerError); +#endif + + if (PyErr_Occurred()) { + Py_DECREF(m); + m = NULL; + } + + finally: + return m; +} + +static void +finisignal(void) +{ + int i; + PyObject *func; + + PyOS_setsig(SIGINT, old_siginthandler); + old_siginthandler = SIG_DFL; + + for (i = 1; i < NSIG; i++) { + func = Handlers[i].func; + Handlers[i].tripped = 0; + Handlers[i].func = NULL; + if (i != SIGINT && func != NULL && func != Py_None && + func != DefaultHandler && func != IgnoreHandler) + PyOS_setsig(i, SIG_DFL); + Py_XDECREF(func); + } + + Py_XDECREF(IntHandler); + IntHandler = NULL; + Py_XDECREF(DefaultHandler); + DefaultHandler = NULL; + Py_XDECREF(IgnoreHandler); + IgnoreHandler = NULL; +} + + +/* Declared in pyerrors.h */ +int +PyErr_CheckSignals(void) +{ + int i; + PyObject *f; + + if (!is_tripped) + return 0; + +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + + /* + * The is_stripped variable is meant to speed up the calls to + * PyErr_CheckSignals (both directly or via pending calls) when no + * signal has arrived. This variable is set to 1 when a signal arrives + * and it is set to 0 here, when we know some signals arrived. This way + * we can run the registered handlers with no signals blocked. + * + * NOTE: with this approach we can have a situation where is_tripped is + * 1 but we have no more signals to handle (Handlers[i].tripped + * is 0 for every signal i). This won't do us any harm (except + * we're gonna spent some cycles for nothing). This happens when + * we receive a signal i after we zero is_tripped and before we + * check Handlers[i].tripped. + */ + is_tripped = 0; + + if (!(f = (PyObject *)PyEval_GetFrame())) + f = Py_None; + + for (i = 1; i < NSIG; i++) { + if (Handlers[i].tripped) { + PyObject *result = NULL; + PyObject *arglist = Py_BuildValue("(iO)", i, f); + Handlers[i].tripped = 0; + + if (arglist) { + result = PyEval_CallObject(Handlers[i].func, + arglist); + Py_DECREF(arglist); + } + if (!result) + return -1; + + Py_DECREF(result); + } + } + + return 0; +} + + +/* Replacements for intrcheck.c functionality + * Declared in pyerrors.h + */ +void +PyErr_SetInterrupt(void) +{ + is_tripped = 1; + Handlers[SIGINT].tripped = 1; + Py_AddPendingCall((int (*)(void *))PyErr_CheckSignals, NULL); +} + +void +PyOS_InitInterrupts(void) +{ + PyObject *m = PyInit_signal(); + if (m) { + _PyImport_FixupExtension(m, "signal", "signal"); + Py_DECREF(m); + } +} + +void +PyOS_FiniInterrupts(void) +{ + finisignal(); +} + +int +PyOS_InterruptOccurred(void) +{ + if (Handlers[SIGINT].tripped) { +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + Handlers[SIGINT].tripped = 0; + return 1; + } + return 0; +} + +void +PyOS_AfterFork(void) +{ +#ifdef WITH_THREAD + PyEval_ReInitThreads(); + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); + _PyImport_ReInitLock(); + PyThread_ReInitTLS(); +#endif +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_1c76b7_d6056b_Modules#faulthandler.c b/src/main/resource/testFiles/cpython/prevFiles/prev_1c76b7_d6056b_Modules#faulthandler.c new file mode 100644 index 0000000..e8ed3e8 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_1c76b7_d6056b_Modules#faulthandler.c @@ -0,0 +1,998 @@ +#include "Python.h" +#include "pythread.h" +#include +#include +#include +#include + +#ifdef WITH_THREAD +# define FAULTHANDLER_LATER +#endif + +#ifndef MS_WINDOWS + /* register() is useless on Windows, because only SIGSEGV and SIGILL can be + handled by the process, and these signals can only be used with enable(), + not using register() */ +# define FAULTHANDLER_USER +#endif + +/* Allocate at maximum 100 MB of the stack to raise the stack overflow */ +#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024) + +#define PUTS(fd, str) write(fd, str, strlen(str)) + +#ifdef HAVE_SIGACTION +typedef struct sigaction _Py_sighandler_t; +#else +typedef PyOS_sighandler_t _Py_sighandler_t; +#endif + +typedef struct { + int signum; + int enabled; + const char* name; + _Py_sighandler_t previous; + int all_threads; +} fault_handler_t; + +static struct { + int enabled; + PyObject *file; + int fd; + int all_threads; +} fatal_error = {0, NULL, -1, 0}; + +#ifdef FAULTHANDLER_LATER +static struct { + PyObject *file; + int fd; + PY_TIMEOUT_T timeout_ms; /* timeout in microseconds */ + int repeat; + volatile int running; + PyInterpreterState *interp; + int exit; + /* released by parent thread when cancel request */ + PyThread_type_lock cancel_event; + /* released by child thread when joined */ + PyThread_type_lock join_event; +} thread; +#endif + +#ifdef FAULTHANDLER_USER +typedef struct { + int enabled; + PyObject *file; + int fd; + int all_threads; + _Py_sighandler_t previous; +} user_signal_t; + +static user_signal_t *user_signals; + +/* the following macros come from Python: Modules/signalmodule.c */ +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#endif +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + +#endif /* FAULTHANDLER_USER */ + + +static fault_handler_t faulthandler_handlers[] = { +#ifdef SIGBUS + {SIGBUS, 0, "Bus error", }, +#endif +#ifdef SIGILL + {SIGILL, 0, "Illegal instruction", }, +#endif + {SIGFPE, 0, "Floating point exception", }, + /* define SIGSEGV at the end to make it the default choice if searching the + handler fails in faulthandler_fatal_error() */ + {SIGSEGV, 0, "Segmentation fault", } +}; +static const unsigned char faulthandler_nsignals = \ + sizeof(faulthandler_handlers) / sizeof(faulthandler_handlers[0]); + +#ifdef HAVE_SIGALTSTACK +static stack_t stack; +#endif + + +/* Get the file descriptor of a file by calling its fileno() method and then + call its flush() method. + + If file is NULL or Py_None, use sys.stderr as the new file. + + On success, return the new file and write the file descriptor into *p_fd. + On error, return NULL. */ + +static PyObject* +faulthandler_get_fileno(PyObject *file, int *p_fd) +{ + PyObject *result; + long fd_long; + int fd; + + if (file == NULL || file == Py_None) { + file = PySys_GetObject("stderr"); + if (file == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr"); + return NULL; + } + } + + result = PyObject_CallMethod(file, "fileno", ""); + if (result == NULL) + return NULL; + + fd = -1; + if (PyLong_Check(result)) { + fd_long = PyLong_AsLong(result); + if (0 <= fd_long && fd_long < INT_MAX) + fd = (int)fd_long; + } + Py_DECREF(result); + + if (fd == -1) { + PyErr_SetString(PyExc_RuntimeError, + "file.fileno() is not a valid file descriptor"); + return NULL; + } + + result = PyObject_CallMethod(file, "flush", ""); + if (result != NULL) + Py_DECREF(result); + else { + /* ignore flush() error */ + PyErr_Clear(); + } + *p_fd = fd; + return file; +} + +static PyObject* +faulthandler_dump_traceback_py(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 0; + PyThreadState *tstate; + const char *errmsg; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:dump_traceback", kwlist, + &file, &all_threads)) + return NULL; + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + /* The caller holds the GIL and so PyThreadState_Get() can be used */ + tstate = PyThreadState_Get(); + if (tstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "unable to get the current thread state"); + return NULL; + } + + if (all_threads) { + errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + if (errmsg != NULL) { + PyErr_SetString(PyExc_RuntimeError, errmsg); + return NULL; + } + } + else { + _Py_DumpTraceback(fd, tstate); + } + Py_RETURN_NONE; +} + + +/* Handler of SIGSEGV, SIGFPE, SIGBUS and SIGILL signals. + + Display the current Python traceback, restore the previous handler and call + the previous handler. + + On Windows, don't call explictly the previous handler, because Windows + signal handler would not be called (for an unknown reason). The execution of + the program continues at faulthandler_fatal_error() exit, but the same + instruction will raise the same fault (signal), and so the previous handler + will be called. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_fatal_error( + int signum +#ifdef HAVE_SIGACTION + , siginfo_t *siginfo, void *ucontext +#endif +) +{ + const int fd = fatal_error.fd; + unsigned int i; + fault_handler_t *handler = NULL; + PyThreadState *tstate; + + if (!fatal_error.enabled) + return; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + if (handler->signum == signum) + break; + } + if (handler == NULL) { + /* faulthandler_nsignals == 0 (unlikely) */ + return; + } + + /* restore the previous handler */ +#ifdef HAVE_SIGACTION + (void)sigaction(handler->signum, &handler->previous, NULL); +#else + (void)signal(handler->signum, handler->previous); +#endif + handler->enabled = 0; + + PUTS(fd, "Fatal Python error: "); + PUTS(fd, handler->name); + PUTS(fd, "\n\n"); + + /* SIGSEGV, SIGFPE, SIGBUS and SIGILL are synchronous signals and so are + delivered to the thread that caused the fault. Get the Python thread + state of the current thread. + + PyThreadState_Get() doesn't give the state of the thread that caused the + fault if the thread released the GIL, and so this function cannot be + used. Read the thread local storage (TLS) instead: call + PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) + return; + + if (fatal_error.all_threads) + _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + else + _Py_DumpTraceback(fd, tstate); + +#ifndef MS_WINDOWS + /* call the previous signal handler: it is called if we use sigaction() + thanks to SA_NODEFER flag, otherwise it is deferred */ + raise(signum); +#else + /* on Windows, don't call explictly the previous handler, because Windows + signal handler would not be called */ +#endif +} + +/* Install handler for fatal signals (SIGSEGV, SIGFPE, ...). */ + +static PyObject* +faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 0; + unsigned int i; + fault_handler_t *handler; +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + int err; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:enable", kwlist, &file, &all_threads)) + return NULL; + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + Py_XDECREF(fatal_error.file); + Py_INCREF(file); + fatal_error.file = file; + fatal_error.fd = fd; + fatal_error.all_threads = all_threads; + + if (!fatal_error.enabled) { + fatal_error.enabled = 1; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; +#ifdef HAVE_SIGACTION + action.sa_sigaction = faulthandler_fatal_error; + sigemptyset(&action.sa_mask); + /* Do not prevent the signal from being received from within + its own signal handler */ + action.sa_flags = SA_NODEFER; +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + /* Call the signal handler on an alternate signal stack + provided by sigaltstack() */ + action.sa_flags |= SA_ONSTACK; + } +#endif + err = sigaction(handler->signum, &action, &handler->previous); +#else + handler->previous = signal(handler->signum, + faulthandler_fatal_error); + err = (handler->previous == SIG_ERR); +#endif + if (err) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + handler->enabled = 1; + } + } + Py_RETURN_NONE; +} + +static void +faulthandler_disable(void) +{ + unsigned int i; + fault_handler_t *handler; + + if (fatal_error.enabled) { + fatal_error.enabled = 0; + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + if (!handler->enabled) + continue; +#ifdef HAVE_SIGACTION + (void)sigaction(handler->signum, &handler->previous, NULL); +#else + (void)signal(handler->signum, handler->previous); +#endif + handler->enabled = 0; + } + } + + Py_CLEAR(fatal_error.file); +} + +static PyObject* +faulthandler_disable_py(PyObject *self) +{ + if (!fatal_error.enabled) { + Py_INCREF(Py_False); + return Py_False; + } + faulthandler_disable(); + Py_INCREF(Py_True); + return Py_True; +} + +static PyObject* +faulthandler_is_enabled(PyObject *self) +{ + return PyBool_FromLong(fatal_error.enabled); +} + +#ifdef FAULTHANDLER_LATER + +static void +faulthandler_thread(void *unused) +{ + PyLockStatus st; + const char* errmsg; + PyThreadState *current; + int ok; + + do { + st = PyThread_acquire_lock_timed(thread.cancel_event, + thread.timeout_ms, 0); + if (st == PY_LOCK_ACQUIRED) { + /* Cancelled by user */ + break; + } + /* Timeout => dump traceback */ + assert(st == PY_LOCK_FAILURE); + + /* get the thread holding the GIL, NULL if no thread hold the GIL */ + current = _Py_atomic_load_relaxed(&_PyThreadState_Current); + + errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current); + ok = (errmsg == NULL); + + if (thread.exit) + _exit(1); + } while (ok && thread.repeat); + + /* The only way out */ + thread.running = 0; + PyThread_release_lock(thread.join_event); + PyThread_release_lock(thread.cancel_event); +} + +static void +faulthandler_cancel_dump_tracebacks_later(void) +{ + if (thread.running) { + /* Notify cancellation */ + PyThread_release_lock(thread.cancel_event); + /* Wait for thread to join */ + PyThread_acquire_lock(thread.join_event, 1); + assert(thread.running == 0); + PyThread_release_lock(thread.join_event); + } + Py_CLEAR(thread.file); +} + +static PyObject* +faulthandler_dump_traceback_later(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL}; + double timeout; + PY_TIMEOUT_T timeout_ms; + int repeat = 0; + PyObject *file = NULL; + int fd; + int exit = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "d|iOi:dump_tracebacks_later", kwlist, + &timeout, &repeat, &file, &exit)) + return NULL; + timeout *= 1e6; + if (timeout >= (double) PY_TIMEOUT_MAX) { + PyErr_SetString(PyExc_OverflowError, "timeout value is too large"); + return NULL; + } + timeout_ms = (PY_TIMEOUT_T)timeout; + if (timeout_ms <= 0) { + PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0"); + return NULL; + } + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + /* Cancel previous thread, if running */ + faulthandler_cancel_dump_tracebacks_later(); + + Py_XDECREF(thread.file); + Py_INCREF(file); + thread.file = file; + thread.fd = fd; + thread.timeout_ms = timeout_ms; + thread.repeat = repeat; + thread.interp = PyThreadState_Get()->interp; + thread.exit = exit; + + /* Arm these locks to serve as events when released */ + PyThread_acquire_lock(thread.join_event, 1); + PyThread_acquire_lock(thread.cancel_event, 1); + + thread.running = 1; + if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) { + thread.running = 0; + Py_CLEAR(thread.file); + PyErr_SetString(PyExc_RuntimeError, + "unable to start watchdog thread"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject* +faulthandler_cancel_dump_tracebacks_later_py(PyObject *self) +{ + faulthandler_cancel_dump_tracebacks_later(); + Py_RETURN_NONE; +} +#endif /* FAULTHANDLER_LATER */ + +#ifdef FAULTHANDLER_USER +/* Handler of user signals (e.g. SIGUSR1). + + Dump the traceback of the current thread, or of all threads if + thread.all_threads is true. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_user(int signum) +{ + user_signal_t *user; + PyThreadState *tstate; + + user = &user_signals[signum]; + if (!user->enabled) + return; + + /* PyThreadState_Get() doesn't give the state of the current thread if + the thread doesn't hold the GIL. Read the thread local storage (TLS) + instead: call PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) { + /* unable to get the current thread, do nothing */ + return; + } + + if (user->all_threads) + _Py_DumpTracebackThreads(user->fd, tstate->interp, tstate); + else + _Py_DumpTraceback(user->fd, tstate); +} + +static PyObject* +faulthandler_register(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"signum", "file", "all_threads", NULL}; + int signum; + PyObject *file = NULL; + int all_threads = 0; + int fd; + unsigned int i; + user_signal_t *user; + _Py_sighandler_t previous; +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + int err; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "i|Oi:register", kwlist, + &signum, &file, &all_threads)) + return NULL; + + if (signum < 1 || NSIG <= signum) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } + + for (i=0; i < faulthandler_nsignals; i++) { + if (faulthandler_handlers[i].signum == signum) { + PyErr_Format(PyExc_RuntimeError, + "signal %i cannot be registered by register(), " + "use enable() instead", + signum); + return NULL; + } + } + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + if (user_signals == NULL) { + user_signals = calloc(NSIG, sizeof(user_signal_t)); + if (user_signals == NULL) + return PyErr_NoMemory(); + } + user = &user_signals[signum]; + + if (!user->enabled) { +#ifdef HAVE_SIGACTION + action.sa_handler = faulthandler_user; + sigemptyset(&action.sa_mask); + /* if the signal is received while the kernel is executing a system + call, try to restart the system call instead of interrupting it and + return EINTR */ + action.sa_flags = SA_RESTART; +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + /* Call the signal handler on an alternate signal stack + provided by sigaltstack() */ + action.sa_flags |= SA_ONSTACK; + } +#endif + err = sigaction(signum, &action, &previous); +#else + previous = signal(signum, faulthandler_user); + err = (previous == SIG_ERR); +#endif + if (err) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + } + + Py_XDECREF(user->file); + Py_INCREF(file); + user->file = file; + user->fd = fd; + user->all_threads = all_threads; + user->previous = previous; + user->enabled = 1; + + Py_RETURN_NONE; +} + +static int +faulthandler_unregister(user_signal_t *user, int signum) +{ + if (user->enabled) + return 0; + user->enabled = 0; +#ifdef HAVE_SIGACTION + (void)sigaction(signum, &user->previous, NULL); +#else + (void)signal(signum, user->previous); +#endif + Py_CLEAR(user->file); + user->fd = -1; + return 1; +} + +static PyObject* +faulthandler_unregister_py(PyObject *self, PyObject *args) +{ + int signum; + user_signal_t *user; + int change; + + if (!PyArg_ParseTuple(args, "i:unregister", &signum)) + return NULL; + + if (signum < 1 || NSIG <= signum) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } + + user = &user_signals[signum]; + change = faulthandler_unregister(user, signum); + return PyBool_FromLong(change); +} +#endif /* FAULTHANDLER_USER */ + + +static PyObject * +faulthandler_read_null(PyObject *self, PyObject *args) +{ + int *x = NULL, y; + int release_gil = 0; + if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil)) + return NULL; + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + y = *x; + Py_END_ALLOW_THREADS + } else + y = *x; + return PyLong_FromLong(y); + +} + +static PyObject * +faulthandler_sigsegv(PyObject *self, PyObject *args) +{ +#if defined(MS_WINDOWS) + /* faulthandler_fatal_error() restores the previous signal handler and then + gives back the execution flow to the program. In a normal case, the + SIGSEGV was raised by the kernel because of a fault, and so if the + program retries to execute the same instruction, the fault will be + raised again. + + Here the fault is simulated by a fake SIGSEGV signal raised by the + application. We have to raise SIGSEGV at lease twice: once for + faulthandler_fatal_error(), and one more time for the previous signal + handler. */ + while(1) + raise(SIGSEGV); +#else + raise(SIGSEGV); +#endif + Py_RETURN_NONE; +} + +static PyObject * +faulthandler_sigfpe(PyObject *self, PyObject *args) +{ + /* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on + PowerPC. Use volatile to disable compile-time optimizations. */ + volatile int x = 1, y = 0, z; + z = x / y; + /* if the division by zero didn't raise a SIGFPE, raise it manually */ + raise(SIGFPE); + Py_RETURN_NONE; +} + +#ifdef SIGBUS +static PyObject * +faulthandler_sigbus(PyObject *self, PyObject *args) +{ + raise(SIGBUS); + Py_RETURN_NONE; +} +#endif + +#ifdef SIGILL +static PyObject * +faulthandler_sigill(PyObject *self, PyObject *args) +{ +#if defined(MS_WINDOWS) + /* see faulthandler_sigsegv() for the explanation about while(1) */ + while(1) + raise(SIGILL); +#else + raise(SIGILL); +#endif + Py_RETURN_NONE; +} +#endif + +static PyObject * +faulthandler_fatal_error_py(PyObject *self, PyObject *args) +{ + char *message; + if (!PyArg_ParseTuple(args, "y:fatal_error", &message)) + return NULL; + Py_FatalError(message); + Py_RETURN_NONE; +} + +#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) +void* +stack_overflow(void *min_sp, void *max_sp, size_t *depth) +{ + /* allocate 4096 bytes on the stack at each call */ + unsigned char buffer[4096]; + void *sp = &buffer; + *depth += 1; + if (sp < min_sp || max_sp < sp) + return sp; + buffer[0] = 1; + buffer[4095] = 0; + return stack_overflow(min_sp, max_sp, depth); +} + +static PyObject * +faulthandler_stack_overflow(PyObject *self) +{ + size_t depth, size; + void *sp = &depth, *stop; + + depth = 0; + stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE, + sp + STACK_OVERFLOW_MAX_SIZE, + &depth); + if (sp < stop) + size = stop - sp; + else + size = sp - stop; + PyErr_Format(PyExc_RuntimeError, + "unable to raise a stack overflow (allocated %zu bytes " + "on the stack, %zu recursive calls)", + size, depth); + return NULL; +} +#endif + + +static int +faulthandler_traverse(PyObject *module, visitproc visit, void *arg) +{ +#ifdef FAULTHANDLER_USER + unsigned int index; +#endif + +#ifdef FAULTHANDLER_LATER + Py_VISIT(thread.file); +#endif +#ifdef FAULTHANDLER_USER + if (user_signals != NULL) { + for (index=0; index < NSIG; index++) + Py_VISIT(user_signals[index].file); + } +#endif + Py_VISIT(fatal_error.file); + return 0; +} + +PyDoc_STRVAR(module_doc, +"faulthandler module."); + +static PyMethodDef module_methods[] = { + {"enable", + (PyCFunction)faulthandler_enable, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("enable(file=sys.stderr, all_threads=False): " + "enable the fault handler")}, + {"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS, + PyDoc_STR("disable(): disable the fault handler")}, + {"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS, + PyDoc_STR("is_enabled()->bool: check if the handler is enabled")}, + {"dump_traceback", + (PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=False): " + "dump the traceback of the current thread, or of all threads " + "if all_threads is True, into file")}, +#ifdef FAULTHANDLER_LATER + {"dump_tracebacks_later", + (PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_tracebacks_later(timeout, repeat=False, file=sys.stderr):\n" + "dump the traceback of all threads in timeout seconds,\n" + "or each timeout seconds if repeat is True.")}, + {"cancel_dump_tracebacks_later", + (PyCFunction)faulthandler_cancel_dump_tracebacks_later_py, METH_NOARGS, + PyDoc_STR("cancel_dump_tracebacks_later():\ncancel the previous call " + "to dump_tracebacks_later().")}, +#endif + +#ifdef FAULTHANDLER_USER + {"register", + (PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("register(signum, file=sys.stderr, all_threads=False): " + "register an handler for the signal 'signum': dump the " + "traceback of the current thread, or of all threads if " + "all_threads is True, into file")}, + {"unregister", + faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("unregister(signum): unregister the handler of the signal " + "'signum' registered by register()")}, +#endif + + {"_read_null", faulthandler_read_null, METH_VARARGS, + PyDoc_STR("_read_null(release_gil=False): read from NULL, raise " + "a SIGSEGV or SIGBUS signal depending on the platform")}, + {"_sigsegv", faulthandler_sigsegv, METH_VARARGS, + PyDoc_STR("_sigsegv(): raise a SIGSEGV signal")}, + {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS, + PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, +#ifdef SIGBUS + {"_sigbus", (PyCFunction)faulthandler_sigbus, METH_NOARGS, + PyDoc_STR("_sigbus(): raise a SIGBUS signal")}, +#endif +#ifdef SIGILL + {"_sigill", (PyCFunction)faulthandler_sigill, METH_NOARGS, + PyDoc_STR("_sigill(): raise a SIGILL signal")}, +#endif + {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, + PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, +#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) + {"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS, + PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")}, +#endif + {NULL, NULL} /* terminator */ +}; + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "faulthandler", + module_doc, + 0, /* non negative size to be able to unload the module */ + module_methods, + NULL, + faulthandler_traverse, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_faulthandler(void) +{ + return PyModule_Create(&module_def); +} + +/* Call faulthandler.enable() if PYTHONFAULTHANDLER environment variable is + defined, or if sys._xoptions has a 'faulthandler' key. */ + +static int +faulthandler_env_options(void) +{ + PyObject *xoptions, *key, *module, *res; + int enable; + + if (!Py_GETENV("PYTHONFAULTHANDLER")) { + xoptions = PySys_GetXOptions(); + if (xoptions == NULL) + return -1; + + key = PyUnicode_FromString("faulthandler"); + if (key == NULL) + return -1; + + enable = PyDict_Contains(xoptions, key); + Py_DECREF(key); + if (!enable) + return 0; + } + else + enable = 1; + + module = PyImport_ImportModule("faulthandler"); + if (module == NULL) { + return -1; + } + res = PyObject_CallMethod(module, "enable", ""); + Py_DECREF(module); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +int _PyFaulthandler_Init(void) +{ +#ifdef HAVE_SIGALTSTACK + int err; + + /* Try to allocate an alternate stack for faulthandler() signal handler to + * be able to allocate memory on the stack, even on a stack overflow. If it + * fails, ignore the error. */ + stack.ss_flags = 0; + stack.ss_size = SIGSTKSZ; + stack.ss_sp = PyMem_Malloc(stack.ss_size); + if (stack.ss_sp != NULL) { + err = sigaltstack(&stack, NULL); + if (err) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } + } +#endif +#ifdef FAULTHANDLER_LATER + thread.running = 0; + thread.file = NULL; + thread.cancel_event = PyThread_allocate_lock(); + thread.join_event = PyThread_allocate_lock(); + if (!thread.cancel_event || !thread.join_event) { + PyErr_SetString(PyExc_RuntimeError, + "could not allocate locks for faulthandler"); + return -1; + } +#endif + + return faulthandler_env_options(); +} + +void _PyFaulthandler_Fini(void) +{ +#ifdef FAULTHANDLER_USER + unsigned int i; +#endif + +#ifdef FAULTHANDLER_LATER + /* later */ + faulthandler_cancel_dump_tracebacks_later(); + if (thread.cancel_event) { + PyThread_free_lock(thread.cancel_event); + thread.cancel_event = NULL; + } + if (thread.join_event) { + PyThread_free_lock(thread.join_event); + thread.join_event = NULL; + } +#endif + +#ifdef FAULTHANDLER_USER + /* user */ + if (user_signals != NULL) { + for (i=0; i < NSIG; i++) + faulthandler_unregister(&user_signals[i], i+1); + free(user_signals); + user_signals = NULL; + } +#endif + + /* fatal */ + faulthandler_disable(); +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } +#endif +} diff --git a/src/main/resource/testFiles/cpython/prevFiles/prev_58fb90_c9e435_Modules#_stringio.c b/src/main/resource/testFiles/cpython/prevFiles/prev_58fb90_c9e435_Modules#_stringio.c new file mode 100644 index 0000000..d29fe64 --- /dev/null +++ b/src/main/resource/testFiles/cpython/prevFiles/prev_58fb90_c9e435_Modules#_stringio.c @@ -0,0 +1,383 @@ +#include "Python.h" + +/* This module is a stripped down version of _bytesio.c with a Py_UNICODE + buffer. Most of the functionality is provided by subclassing _StringIO. */ + + +typedef struct { + PyObject_HEAD + Py_UNICODE *buf; + Py_ssize_t pos; + Py_ssize_t string_size; + size_t buf_size; +} StringIOObject; + + +/* Internal routine for changing the size, in terms of characters, of the + buffer of StringIO objects. The caller should ensure that the 'size' + argument is non-negative. Returns 0 on success, -1 otherwise. */ +static int +resize_buffer(StringIOObject *self, size_t size) +{ + /* Here, unsigned types are used to avoid dealing with signed integer + overflow, which is undefined in C. */ + size_t alloc = self->buf_size; + Py_UNICODE *new_buf = NULL; + + assert(self->buf != NULL); + + /* For simplicity, stay in the range of the signed type. Anyway, Python + doesn't allow strings to be longer than this. */ + if (size > PY_SSIZE_T_MAX) + goto overflow; + + if (size < alloc / 2) { + /* Major downsize; resize down to exact size. */ + alloc = size + 1; + } + else if (size < alloc) { + /* Within allocated size; quick exit */ + return 0; + } + else if (size <= alloc * 1.125) { + /* Moderate upsize; overallocate similar to list_resize() */ + alloc = size + (size >> 3) + (size < 9 ? 3 : 6); + } + else { + /* Major upsize; resize up to exact size */ + alloc = size + 1; + } + + if (alloc > ((size_t)-1) / sizeof(Py_UNICODE)) + goto overflow; + new_buf = (Py_UNICODE *)PyMem_Realloc(self->buf, + alloc * sizeof(Py_UNICODE)); + if (new_buf == NULL) { + PyErr_NoMemory(); + return -1; + } + self->buf_size = alloc; + self->buf = new_buf; + + return 0; + + overflow: + PyErr_SetString(PyExc_OverflowError, + "new buffer size too large"); + return -1; +} + +/* Internal routine for writing a string of characters to the buffer of a + StringIO object. Returns the number of bytes wrote, or -1 on error. */ +static Py_ssize_t +write_str(StringIOObject *self, const Py_UNICODE *str, Py_ssize_t len) +{ + assert(self->buf != NULL); + assert(self->pos >= 0); + assert(len >= 0); + + /* This overflow check is not strictly necessary. However, it avoids us to + deal with funky things like comparing an unsigned and a signed + integer. */ + if (self->pos > PY_SSIZE_T_MAX - len) { + PyErr_SetString(PyExc_OverflowError, + "new position too large"); + return -1; + } + if (self->pos + len > self->string_size) { + if (resize_buffer(self, self->pos + len) < 0) + return -1; + } + + if (self->pos > self->string_size) { + /* In case of overseek, pad with null bytes the buffer region between + the end of stream and the current position. + + 0 lo string_size hi + | |<---used--->|<----------available----------->| + | | <--to pad-->|<---to write---> | + 0 buf positon + + */ + memset(self->buf + self->string_size, '\0', + (self->pos - self->string_size) * sizeof(Py_UNICODE)); + } + + /* Copy the data to the internal buffer, overwriting some of the + existing data if self->pos < self->string_size. */ + memcpy(self->buf + self->pos, str, len * sizeof(Py_UNICODE)); + self->pos += len; + + /* Set the new length of the internal string if it has changed */ + if (self->string_size < self->pos) { + self->string_size = self->pos; + } + + return len; +} + +static PyObject * +stringio_getvalue(StringIOObject *self) +{ + return PyUnicode_FromUnicode(self->buf, self->string_size); +} + +static PyObject * +stringio_tell(StringIOObject *self) +{ + return PyLong_FromSsize_t(self->pos); +} + +static PyObject * +stringio_read(StringIOObject *self, PyObject *args) +{ + Py_ssize_t size, n; + Py_UNICODE *output; + PyObject *arg = Py_None; + + if (!PyArg_ParseTuple(args, "|O:read", &arg)) + return NULL; + + if (PyLong_Check(arg)) { + size = PyLong_AsSsize_t(arg); + } + else if (arg == Py_None) { + /* Read until EOF is reached, by default. */ + size = -1; + } + else { + PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", + Py_TYPE(arg)->tp_name); + return NULL; + } + + /* adjust invalid sizes */ + n = self->string_size - self->pos; + if (size < 0 || size > n) { + size = n; + if (size < 0) + size = 0; + } + + assert(self->buf != NULL); + output = self->buf + self->pos; + self->pos += size; + + return PyUnicode_FromUnicode(output, size); +} + +static PyObject * +stringio_truncate(StringIOObject *self, PyObject *args) +{ + Py_ssize_t size; + PyObject *arg = Py_None; + + if (!PyArg_ParseTuple(args, "|O:truncate", &arg)) + return NULL; + + if (PyLong_Check(arg)) { + size = PyLong_AsSsize_t(arg); + if (size == -1 && PyErr_Occurred()) + return NULL; + if (size == -1 && PyErr_Occurred()) + return NULL; + } + else if (arg == Py_None) { + /* Truncate to current position if no argument is passed. */ + size = self->pos; + } + else { + PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", + Py_TYPE(arg)->tp_name); + return NULL; + } + + if (size < 0) { + PyErr_Format(PyExc_ValueError, + "Negative size value %zd", size); + return NULL; + } + + if (size < self->string_size) { + self->string_size = size; + if (resize_buffer(self, size) < 0) + return NULL; + } + self->pos = size; + + return PyLong_FromSsize_t(size); +} + +static PyObject * +stringio_seek(StringIOObject *self, PyObject *args) +{ + Py_ssize_t pos; + int mode = 0; + + if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode)) + return NULL; + + if (mode != 0 && mode != 1 && mode != 2) { + PyErr_Format(PyExc_ValueError, + "Invalid whence (%i, should be 0, 1 or 2)", mode); + return NULL; + } + else if (pos < 0 && mode == 0) { + PyErr_Format(PyExc_ValueError, + "Negative seek position %zd", pos); + return NULL; + } + else if (mode != 0 && pos != 0) { + PyErr_SetString(PyExc_IOError, + "Can't do nonzero cur-relative seeks"); + return NULL; + } + + /* mode 0: offset relative to beginning of the string. + mode 1: no change to current position. + mode 2: change position to end of file. */ + if (mode == 1) { + pos = self->pos; + } + else if (mode == 2) { + pos = self->string_size; + } + + self->pos = pos; + + return PyLong_FromSsize_t(self->pos); +} + +static PyObject * +stringio_write(StringIOObject *self, PyObject *obj) +{ + const Py_UNICODE *str; + Py_ssize_t size; + Py_ssize_t n = 0; + + if (PyUnicode_Check(obj)) { + str = PyUnicode_AsUnicode(obj); + size = PyUnicode_GetSize(obj); + } + else { + PyErr_Format(PyExc_TypeError, "string argument expected, got '%s'", + Py_TYPE(obj)->tp_name); + return NULL; + } + + if (size != 0) { + n = write_str(self, str, size); + if (n < 0) + return NULL; + } + + return PyLong_FromSsize_t(n); +} + +static void +stringio_dealloc(StringIOObject *self) +{ + PyMem_Free(self->buf); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +stringio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + StringIOObject *self; + + assert(type != NULL && type->tp_alloc != NULL); + self = (StringIOObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + self->string_size = 0; + self->pos = 0; + self->buf_size = 0; + self->buf = (Py_UNICODE *)PyMem_Malloc(0); + if (self->buf == NULL) { + Py_DECREF(self); + return PyErr_NoMemory(); + } + + return (PyObject *)self; +} + +static struct PyMethodDef stringio_methods[] = { + {"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, NULL}, + {"read", (PyCFunction)stringio_read, METH_VARARGS, NULL}, + {"tell", (PyCFunction)stringio_tell, METH_NOARGS, NULL}, + {"truncate", (PyCFunction)stringio_truncate, METH_VARARGS, NULL}, + {"seek", (PyCFunction)stringio_seek, METH_VARARGS, NULL}, + {"write", (PyCFunction)stringio_write, METH_O, NULL}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject StringIO_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_stringio._StringIO", /*tp_name*/ + sizeof(StringIOObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)stringio_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + stringio_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + stringio_new, /*tp_new*/ +}; + +static struct PyModuleDef _stringiomodule = { + PyModuleDef_HEAD_INIT, + "_stringio", + NULL, + -1, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__stringio(void) +{ + PyObject *m; + + if (PyType_Ready(&StringIO_Type) < 0) + return NULL; + m = PyModule_Create(&_stringiomodule); + if (m == NULL) + return NULL; + Py_INCREF(&StringIO_Type); + if (PyModule_AddObject(m, "_StringIO", (PyObject *)&StringIO_Type) < 0) + return NULL; + return m; +} diff --git a/src/main/resource/testFiles/cpython/revFiles/1a8501_c47253_Modules#signalmodule.c b/src/main/resource/testFiles/cpython/revFiles/1a8501_c47253_Modules#signalmodule.c new file mode 100644 index 0000000..7464eae --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/1a8501_c47253_Modules#signalmodule.c @@ -0,0 +1,947 @@ + +/* Signal module -- many thanks to Lance Ellinghaus */ + +/* XXX Signals should be recorded per thread, now we have thread state. */ + +#include "Python.h" +#include "intrcheck.h" + +#ifdef MS_WINDOWS +#include +#endif + +#include + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifndef SIG_ERR +#define SIG_ERR ((PyOS_sighandler_t)(-1)) +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#include +#endif + +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + + +/* + NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS + + When threads are supported, we want the following semantics: + + - only the main thread can set a signal handler + - any thread can get a signal handler + - signals are only delivered to the main thread + + I.e. we don't support "synchronous signals" like SIGFPE (catching + this doesn't make much sense in Python anyway) nor do we support + signals as a means of inter-thread communication, since not all + thread implementations support that (at least our thread library + doesn't). + + We still have the problem that in some implementations signals + generated by the keyboard (e.g. SIGINT) are delivered to all + threads (e.g. SGI), while in others (e.g. Solaris) such signals are + delivered to one random thread (an intermediate possibility would + be to deliver it to the main thread -- POSIX?). For now, we have + a working implementation that works in all three cases -- the + handler ignores signals if getpid() isn't the same as in the main + thread. XXX This is a hack. + + GNU pth is a user-space threading library, and as such, all threads + run within the same process. In this case, if the currently running + thread is not the main_thread, send the signal to the main_thread. +*/ + +#ifdef WITH_THREAD +#include /* For pid_t */ +#include "pythread.h" +static long main_thread; +static pid_t main_pid; +#endif + +static struct { + int tripped; + PyObject *func; +} Handlers[NSIG]; + +static sig_atomic_t wakeup_fd = -1; + +/* Speed up sigcheck() when none tripped */ +static volatile sig_atomic_t is_tripped = 0; + +static PyObject *DefaultHandler; +static PyObject *IgnoreHandler; +static PyObject *IntHandler; + +/* On Solaris 8, gcc will produce a warning that the function + declaration is not a prototype. This is caused by the definition of + SIG_DFL as (void (*)())0; the correct declaration would have been + (void (*)(int))0. */ + +static PyOS_sighandler_t old_siginthandler = SIG_DFL; + +#ifdef HAVE_GETITIMER +static PyObject *ItimerError; + +/* auxiliary functions for setitimer/getitimer */ +static void +timeval_from_double(double d, struct timeval *tv) +{ + tv->tv_sec = floor(d); + tv->tv_usec = fmod(d, 1.0) * 1000000.0; +} + +Py_LOCAL_INLINE(double) +double_from_timeval(struct timeval *tv) +{ + return tv->tv_sec + (double)(tv->tv_usec / 1000000.0); +} + +static PyObject * +itimer_retval(struct itimerval *iv) +{ + PyObject *r, *v; + + r = PyTuple_New(2); + if (r == NULL) + return NULL; + + if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) { + Py_DECREF(r); + return NULL; + } + + PyTuple_SET_ITEM(r, 0, v); + + if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) { + Py_DECREF(r); + return NULL; + } + + PyTuple_SET_ITEM(r, 1, v); + + return r; +} +#endif + +static PyObject * +signal_default_int_handler(PyObject *self, PyObject *args) +{ + PyErr_SetNone(PyExc_KeyboardInterrupt); + return NULL; +} + +PyDoc_STRVAR(default_int_handler_doc, +"default_int_handler(...)\n\ +\n\ +The default handler for SIGINT installed by Python.\n\ +It raises KeyboardInterrupt."); + + +static int +checksignals_witharg(void * unused) +{ + return PyErr_CheckSignals(); +} + +static void +signal_handler(int sig_num) +{ +#ifdef WITH_THREAD +#ifdef WITH_PTH + if (PyThread_get_thread_ident() != main_thread) { + pth_raise(*(pth_t *) main_thread, sig_num); + return; + } +#endif + /* See NOTES section above */ + if (getpid() == main_pid) { +#endif + Handlers[sig_num].tripped = 1; + /* Set is_tripped after setting .tripped, as it gets + cleared in PyErr_CheckSignals() before .tripped. */ + is_tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); + if (wakeup_fd != -1) + write(wakeup_fd, "\0", 1); +#ifdef WITH_THREAD + } +#endif +#ifdef SIGCHLD + if (sig_num == SIGCHLD) { + /* To avoid infinite recursion, this signal remains + reset until explicit re-instated. + Don't clear the 'func' field as it is our pointer + to the Python handler... */ + return; + } +#endif + PyOS_setsig(sig_num, signal_handler); +} + + +#ifdef HAVE_ALARM +static PyObject * +signal_alarm(PyObject *self, PyObject *args) +{ + int t; + if (!PyArg_ParseTuple(args, "i:alarm", &t)) + return NULL; + /* alarm() returns the number of seconds remaining */ + return PyLong_FromLong((long)alarm(t)); +} + +PyDoc_STRVAR(alarm_doc, +"alarm(seconds)\n\ +\n\ +Arrange for SIGALRM to arrive after the given number of seconds."); +#endif + +#ifdef HAVE_PAUSE +static PyObject * +signal_pause(PyObject *self) +{ + Py_BEGIN_ALLOW_THREADS + (void)pause(); + Py_END_ALLOW_THREADS + /* make sure that any exceptions that got raised are propagated + * back into Python + */ + if (PyErr_CheckSignals()) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} +PyDoc_STRVAR(pause_doc, +"pause()\n\ +\n\ +Wait until a signal arrives."); + +#endif + + +static PyObject * +signal_signal(PyObject *self, PyObject *args) +{ + PyObject *obj; + int sig_num; + PyObject *old_handler; + void (*func)(int); + if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) + return NULL; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) { + PyErr_SetString(PyExc_ValueError, + "signal only works in main thread"); + return NULL; + } +#endif + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (obj == IgnoreHandler) + func = SIG_IGN; + else if (obj == DefaultHandler) + func = SIG_DFL; + else if (!PyCallable_Check(obj)) { + PyErr_SetString(PyExc_TypeError, +"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"); + return NULL; + } + else + func = signal_handler; + if (PyOS_setsig(sig_num, func) == SIG_ERR) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + old_handler = Handlers[sig_num].func; + Handlers[sig_num].tripped = 0; + Py_INCREF(obj); + Handlers[sig_num].func = obj; + return old_handler; +} + +PyDoc_STRVAR(signal_doc, +"signal(sig, action) -> action\n\ +\n\ +Set the action for the given signal. The action can be SIG_DFL,\n\ +SIG_IGN, or a callable Python object. The previous action is\n\ +returned. See getsignal() for possible return values.\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + + +static PyObject * +signal_getsignal(PyObject *self, PyObject *args) +{ + int sig_num; + PyObject *old_handler; + if (!PyArg_ParseTuple(args, "i:getsignal", &sig_num)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + old_handler = Handlers[sig_num].func; + Py_INCREF(old_handler); + return old_handler; +} + +PyDoc_STRVAR(getsignal_doc, +"getsignal(sig) -> action\n\ +\n\ +Return the current action for the given signal. The return value can be:\n\ +SIG_IGN -- if the signal is being ignored\n\ +SIG_DFL -- if the default action for the signal is in effect\n\ +None -- if an unknown handler is in effect\n\ +anything else -- the callable Python object used as a handler"); + +#ifdef HAVE_SIGINTERRUPT +PyDoc_STRVAR(siginterrupt_doc, +"siginterrupt(sig, flag) -> None\n\ +change system call restart behaviour: if flag is False, system calls\n\ +will be restarted when interrupted by signal sig, else system calls\n\ +will be interrupted."); + +static PyObject * +signal_siginterrupt(PyObject *self, PyObject *args) +{ + int sig_num; + int flag; + + if (!PyArg_ParseTuple(args, "ii:siginterrupt", &sig_num, &flag)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (siginterrupt(sig_num, flag)<0) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +static PyObject * +signal_set_wakeup_fd(PyObject *self, PyObject *args) +{ + struct stat buf; + int fd, old_fd; + if (!PyArg_ParseTuple(args, "i:set_wakeup_fd", &fd)) + return NULL; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) { + PyErr_SetString(PyExc_ValueError, + "set_wakeup_fd only works in main thread"); + return NULL; + } +#endif + if (fd != -1 && fstat(fd, &buf) != 0) { + PyErr_SetString(PyExc_ValueError, "invalid fd"); + return NULL; + } + old_fd = wakeup_fd; + wakeup_fd = fd; + return PyLong_FromLong(old_fd); +} + +PyDoc_STRVAR(set_wakeup_fd_doc, +"set_wakeup_fd(fd) -> fd\n\ +\n\ +Sets the fd to be written to (with '\\0') when a signal\n\ +comes in. A library can use this to wakeup select or poll.\n\ +The previous fd is returned.\n\ +\n\ +The fd must be non-blocking."); + +/* C API for the same, without all the error checking */ +int +PySignal_SetWakeupFd(int fd) +{ + int old_fd = wakeup_fd; + if (fd < 0) + fd = -1; + wakeup_fd = fd; + return old_fd; +} + + +#ifdef HAVE_SETITIMER +static PyObject * +signal_setitimer(PyObject *self, PyObject *args) +{ + double first; + double interval = 0; + int which; + struct itimerval new, old; + + if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval)) + return NULL; + + timeval_from_double(first, &new.it_value); + timeval_from_double(interval, &new.it_interval); + /* Let OS check "which" value */ + if (setitimer(which, &new, &old) != 0) { + PyErr_SetFromErrno(ItimerError); + return NULL; + } + + return itimer_retval(&old); +} + +PyDoc_STRVAR(setitimer_doc, +"setitimer(which, seconds[, interval])\n\ +\n\ +Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\ +or ITIMER_PROF) to fire after value seconds and after\n\ +that every interval seconds.\n\ +The itimer can be cleared by setting seconds to zero.\n\ +\n\ +Returns old values as a tuple: (delay, interval)."); +#endif + + +#ifdef HAVE_GETITIMER +static PyObject * +signal_getitimer(PyObject *self, PyObject *args) +{ + int which; + struct itimerval old; + + if (!PyArg_ParseTuple(args, "i:getitimer", &which)) + return NULL; + + if (getitimer(which, &old) != 0) { + PyErr_SetFromErrno(ItimerError); + return NULL; + } + + return itimer_retval(&old); +} + +PyDoc_STRVAR(getitimer_doc, +"getitimer(which)\n\ +\n\ +Returns current value of given itimer."); +#endif + + +/* List of functions defined in the module */ +static PyMethodDef signal_methods[] = { +#ifdef HAVE_ALARM + {"alarm", signal_alarm, METH_VARARGS, alarm_doc}, +#endif +#ifdef HAVE_SETITIMER + {"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc}, +#endif +#ifdef HAVE_GETITIMER + {"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc}, +#endif + {"signal", signal_signal, METH_VARARGS, signal_doc}, + {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, + {"set_wakeup_fd", signal_set_wakeup_fd, METH_VARARGS, set_wakeup_fd_doc}, +#ifdef HAVE_SIGINTERRUPT + {"siginterrupt", signal_siginterrupt, METH_VARARGS, siginterrupt_doc}, +#endif +#ifdef HAVE_PAUSE + {"pause", (PyCFunction)signal_pause, + METH_NOARGS,pause_doc}, +#endif + {"default_int_handler", signal_default_int_handler, + METH_VARARGS, default_int_handler_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module provides mechanisms to use signal handlers in Python.\n\ +\n\ +Functions:\n\ +\n\ +alarm() -- cause SIGALRM after a specified time [Unix only]\n\ +setitimer() -- cause a signal (described below) after a specified\n\ + float time and the timer may restart then [Unix only]\n\ +getitimer() -- get current value of timer [Unix only]\n\ +signal() -- set the action for a given signal\n\ +getsignal() -- get the signal action for a given signal\n\ +pause() -- wait until a signal arrives [Unix only]\n\ +default_int_handler() -- default SIGINT handler\n\ +\n\ +signal constants:\n\ +SIG_DFL -- used to refer to the system default handler\n\ +SIG_IGN -- used to ignore the signal\n\ +NSIG -- number of defined signals\n\ +SIGINT, SIGTERM, etc. -- signal numbers\n\ +\n\ +itimer constants:\n\ +ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\ + expiration\n\ +ITIMER_VIRTUAL -- decrements only when the process is executing,\n\ + and delivers SIGVTALRM upon expiration\n\ +ITIMER_PROF -- decrements both when the process is executing and\n\ + when the system is executing on behalf of the process.\n\ + Coupled with ITIMER_VIRTUAL, this timer is usually\n\ + used to profile the time spent by the application\n\ + in user and kernel space. SIGPROF is delivered upon\n\ + expiration.\n\ +\n\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + +static struct PyModuleDef signalmodule = { + PyModuleDef_HEAD_INIT, + "signal", + module_doc, + -1, + signal_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_signal(void) +{ + PyObject *m, *d, *x; + int i; + +#ifdef WITH_THREAD + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); +#endif + + /* Create the module and add the functions */ + m = PyModule_Create(&signalmodule); + if (m == NULL) + return NULL; + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL); + if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0) + goto finally; + + x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN); + if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0) + goto finally; + + x = PyLong_FromLong((long)NSIG); + if (!x || PyDict_SetItemString(d, "NSIG", x) < 0) + goto finally; + Py_DECREF(x); + + x = IntHandler = PyDict_GetItemString(d, "default_int_handler"); + if (!x) + goto finally; + Py_INCREF(IntHandler); + + Handlers[0].tripped = 0; + for (i = 1; i < NSIG; i++) { + void (*t)(int); + t = PyOS_getsig(i); + Handlers[i].tripped = 0; + if (t == SIG_DFL) + Handlers[i].func = DefaultHandler; + else if (t == SIG_IGN) + Handlers[i].func = IgnoreHandler; + else + Handlers[i].func = Py_None; /* None of our business */ + Py_INCREF(Handlers[i].func); + } + if (Handlers[SIGINT].func == DefaultHandler) { + /* Install default int handler */ + Py_INCREF(IntHandler); + Py_DECREF(Handlers[SIGINT].func); + Handlers[SIGINT].func = IntHandler; + old_siginthandler = PyOS_setsig(SIGINT, signal_handler); + } + +#ifdef SIGHUP + x = PyLong_FromLong(SIGHUP); + PyDict_SetItemString(d, "SIGHUP", x); + Py_XDECREF(x); +#endif +#ifdef SIGINT + x = PyLong_FromLong(SIGINT); + PyDict_SetItemString(d, "SIGINT", x); + Py_XDECREF(x); +#endif +#ifdef SIGBREAK + x = PyLong_FromLong(SIGBREAK); + PyDict_SetItemString(d, "SIGBREAK", x); + Py_XDECREF(x); +#endif +#ifdef SIGQUIT + x = PyLong_FromLong(SIGQUIT); + PyDict_SetItemString(d, "SIGQUIT", x); + Py_XDECREF(x); +#endif +#ifdef SIGILL + x = PyLong_FromLong(SIGILL); + PyDict_SetItemString(d, "SIGILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGTRAP + x = PyLong_FromLong(SIGTRAP); + PyDict_SetItemString(d, "SIGTRAP", x); + Py_XDECREF(x); +#endif +#ifdef SIGIOT + x = PyLong_FromLong(SIGIOT); + PyDict_SetItemString(d, "SIGIOT", x); + Py_XDECREF(x); +#endif +#ifdef SIGABRT + x = PyLong_FromLong(SIGABRT); + PyDict_SetItemString(d, "SIGABRT", x); + Py_XDECREF(x); +#endif +#ifdef SIGEMT + x = PyLong_FromLong(SIGEMT); + PyDict_SetItemString(d, "SIGEMT", x); + Py_XDECREF(x); +#endif +#ifdef SIGFPE + x = PyLong_FromLong(SIGFPE); + PyDict_SetItemString(d, "SIGFPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGKILL + x = PyLong_FromLong(SIGKILL); + PyDict_SetItemString(d, "SIGKILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGBUS + x = PyLong_FromLong(SIGBUS); + PyDict_SetItemString(d, "SIGBUS", x); + Py_XDECREF(x); +#endif +#ifdef SIGSEGV + x = PyLong_FromLong(SIGSEGV); + PyDict_SetItemString(d, "SIGSEGV", x); + Py_XDECREF(x); +#endif +#ifdef SIGSYS + x = PyLong_FromLong(SIGSYS); + PyDict_SetItemString(d, "SIGSYS", x); + Py_XDECREF(x); +#endif +#ifdef SIGPIPE + x = PyLong_FromLong(SIGPIPE); + PyDict_SetItemString(d, "SIGPIPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGALRM + x = PyLong_FromLong(SIGALRM); + PyDict_SetItemString(d, "SIGALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGTERM + x = PyLong_FromLong(SIGTERM); + PyDict_SetItemString(d, "SIGTERM", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR1 + x = PyLong_FromLong(SIGUSR1); + PyDict_SetItemString(d, "SIGUSR1", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR2 + x = PyLong_FromLong(SIGUSR2); + PyDict_SetItemString(d, "SIGUSR2", x); + Py_XDECREF(x); +#endif +#ifdef SIGCLD + x = PyLong_FromLong(SIGCLD); + PyDict_SetItemString(d, "SIGCLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGCHLD + x = PyLong_FromLong(SIGCHLD); + PyDict_SetItemString(d, "SIGCHLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGPWR + x = PyLong_FromLong(SIGPWR); + PyDict_SetItemString(d, "SIGPWR", x); + Py_XDECREF(x); +#endif +#ifdef SIGIO + x = PyLong_FromLong(SIGIO); + PyDict_SetItemString(d, "SIGIO", x); + Py_XDECREF(x); +#endif +#ifdef SIGURG + x = PyLong_FromLong(SIGURG); + PyDict_SetItemString(d, "SIGURG", x); + Py_XDECREF(x); +#endif +#ifdef SIGWINCH + x = PyLong_FromLong(SIGWINCH); + PyDict_SetItemString(d, "SIGWINCH", x); + Py_XDECREF(x); +#endif +#ifdef SIGPOLL + x = PyLong_FromLong(SIGPOLL); + PyDict_SetItemString(d, "SIGPOLL", x); + Py_XDECREF(x); +#endif +#ifdef SIGSTOP + x = PyLong_FromLong(SIGSTOP); + PyDict_SetItemString(d, "SIGSTOP", x); + Py_XDECREF(x); +#endif +#ifdef SIGTSTP + x = PyLong_FromLong(SIGTSTP); + PyDict_SetItemString(d, "SIGTSTP", x); + Py_XDECREF(x); +#endif +#ifdef SIGCONT + x = PyLong_FromLong(SIGCONT); + PyDict_SetItemString(d, "SIGCONT", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTIN + x = PyLong_FromLong(SIGTTIN); + PyDict_SetItemString(d, "SIGTTIN", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTOU + x = PyLong_FromLong(SIGTTOU); + PyDict_SetItemString(d, "SIGTTOU", x); + Py_XDECREF(x); +#endif +#ifdef SIGVTALRM + x = PyLong_FromLong(SIGVTALRM); + PyDict_SetItemString(d, "SIGVTALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGPROF + x = PyLong_FromLong(SIGPROF); + PyDict_SetItemString(d, "SIGPROF", x); + Py_XDECREF(x); +#endif +#ifdef SIGXCPU + x = PyLong_FromLong(SIGXCPU); + PyDict_SetItemString(d, "SIGXCPU", x); + Py_XDECREF(x); +#endif +#ifdef SIGXFSZ + x = PyLong_FromLong(SIGXFSZ); + PyDict_SetItemString(d, "SIGXFSZ", x); + Py_XDECREF(x); +#endif +#ifdef SIGRTMIN + x = PyLong_FromLong(SIGRTMIN); + PyDict_SetItemString(d, "SIGRTMIN", x); + Py_XDECREF(x); +#endif +#ifdef SIGRTMAX + x = PyLong_FromLong(SIGRTMAX); + PyDict_SetItemString(d, "SIGRTMAX", x); + Py_XDECREF(x); +#endif +#ifdef SIGINFO + x = PyLong_FromLong(SIGINFO); + PyDict_SetItemString(d, "SIGINFO", x); + Py_XDECREF(x); +#endif + +#ifdef ITIMER_REAL + x = PyLong_FromLong(ITIMER_REAL); + PyDict_SetItemString(d, "ITIMER_REAL", x); + Py_DECREF(x); +#endif +#ifdef ITIMER_VIRTUAL + x = PyLong_FromLong(ITIMER_VIRTUAL); + PyDict_SetItemString(d, "ITIMER_VIRTUAL", x); + Py_DECREF(x); +#endif +#ifdef ITIMER_PROF + x = PyLong_FromLong(ITIMER_PROF); + PyDict_SetItemString(d, "ITIMER_PROF", x); + Py_DECREF(x); +#endif + +#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER) + ItimerError = PyErr_NewException("signal.ItimerError", + PyExc_IOError, NULL); + if (ItimerError != NULL) + PyDict_SetItemString(d, "ItimerError", ItimerError); +#endif + + if (PyErr_Occurred()) { + Py_DECREF(m); + m = NULL; + } + + finally: + return m; +} + +static void +finisignal(void) +{ + int i; + PyObject *func; + + PyOS_setsig(SIGINT, old_siginthandler); + old_siginthandler = SIG_DFL; + + for (i = 1; i < NSIG; i++) { + func = Handlers[i].func; + Handlers[i].tripped = 0; + Handlers[i].func = NULL; + if (i != SIGINT && func != NULL && func != Py_None && + func != DefaultHandler && func != IgnoreHandler) + PyOS_setsig(i, SIG_DFL); + Py_XDECREF(func); + } + + Py_XDECREF(IntHandler); + IntHandler = NULL; + Py_XDECREF(DefaultHandler); + DefaultHandler = NULL; + Py_XDECREF(IgnoreHandler); + IgnoreHandler = NULL; +} + + +/* Declared in pyerrors.h */ +int +PyErr_CheckSignals(void) +{ + int i; + PyObject *f; + + if (!is_tripped) + return 0; + +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + + /* + * The is_stripped variable is meant to speed up the calls to + * PyErr_CheckSignals (both directly or via pending calls) when no + * signal has arrived. This variable is set to 1 when a signal arrives + * and it is set to 0 here, when we know some signals arrived. This way + * we can run the registered handlers with no signals blocked. + * + * NOTE: with this approach we can have a situation where is_tripped is + * 1 but we have no more signals to handle (Handlers[i].tripped + * is 0 for every signal i). This won't do us any harm (except + * we're gonna spent some cycles for nothing). This happens when + * we receive a signal i after we zero is_tripped and before we + * check Handlers[i].tripped. + */ + is_tripped = 0; + + if (!(f = (PyObject *)PyEval_GetFrame())) + f = Py_None; + + for (i = 1; i < NSIG; i++) { + if (Handlers[i].tripped) { + PyObject *result = NULL; + PyObject *arglist = Py_BuildValue("(iO)", i, f); + Handlers[i].tripped = 0; + + if (arglist) { + result = PyEval_CallObject(Handlers[i].func, + arglist); + Py_DECREF(arglist); + } + if (!result) + return -1; + + Py_DECREF(result); + } + } + + return 0; +} + + +/* Replacements for intrcheck.c functionality + * Declared in pyerrors.h + */ +void +PyErr_SetInterrupt(void) +{ + is_tripped = 1; + Handlers[SIGINT].tripped = 1; + Py_AddPendingCall((int (*)(void *))PyErr_CheckSignals, NULL); +} + +void +PyOS_InitInterrupts(void) +{ + PyObject *m = PyInit_signal(); + if (m) { + _PyImport_FixupExtension(m, "signal", "signal"); + Py_DECREF(m); + } +} + +void +PyOS_FiniInterrupts(void) +{ + finisignal(); +} + +int +PyOS_InterruptOccurred(void) +{ + if (Handlers[SIGINT].tripped) { +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + Handlers[SIGINT].tripped = 0; + return 1; + } + return 0; +} + +void +PyOS_AfterFork(void) +{ +#ifdef WITH_THREAD + PyEval_ReInitThreads(); + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); + _PyImport_ReInitLock(); + PyThread_ReInitTLS(); +#endif +} diff --git a/src/main/resource/testFiles/cpython/revFiles/1c76b7_d6056b_Modules#faulthandler.c b/src/main/resource/testFiles/cpython/revFiles/1c76b7_d6056b_Modules#faulthandler.c new file mode 100644 index 0000000..760689e --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/1c76b7_d6056b_Modules#faulthandler.c @@ -0,0 +1,998 @@ +#include "Python.h" +#include "pythread.h" +#include +#include +#include +#include + +#ifdef WITH_THREAD +# define FAULTHANDLER_LATER +#endif + +#ifndef MS_WINDOWS + /* register() is useless on Windows, because only SIGSEGV and SIGILL can be + handled by the process, and these signals can only be used with enable(), + not using register() */ +# define FAULTHANDLER_USER +#endif + +/* Allocate at maximum 100 MB of the stack to raise the stack overflow */ +#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024) + +#define PUTS(fd, str) write(fd, str, strlen(str)) + +#ifdef HAVE_SIGACTION +typedef struct sigaction _Py_sighandler_t; +#else +typedef PyOS_sighandler_t _Py_sighandler_t; +#endif + +typedef struct { + int signum; + int enabled; + const char* name; + _Py_sighandler_t previous; + int all_threads; +} fault_handler_t; + +static struct { + int enabled; + PyObject *file; + int fd; + int all_threads; +} fatal_error = {0, NULL, -1, 0}; + +#ifdef FAULTHANDLER_LATER +static struct { + PyObject *file; + int fd; + PY_TIMEOUT_T timeout_ms; /* timeout in microseconds */ + int repeat; + volatile int running; + PyInterpreterState *interp; + int exit; + /* released by parent thread when cancel request */ + PyThread_type_lock cancel_event; + /* released by child thread when joined */ + PyThread_type_lock join_event; +} thread; +#endif + +#ifdef FAULTHANDLER_USER +typedef struct { + int enabled; + PyObject *file; + int fd; + int all_threads; + _Py_sighandler_t previous; +} user_signal_t; + +static user_signal_t *user_signals; + +/* the following macros come from Python: Modules/signalmodule.c */ +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#endif +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + +#endif /* FAULTHANDLER_USER */ + + +static fault_handler_t faulthandler_handlers[] = { +#ifdef SIGBUS + {SIGBUS, 0, "Bus error", }, +#endif +#ifdef SIGILL + {SIGILL, 0, "Illegal instruction", }, +#endif + {SIGFPE, 0, "Floating point exception", }, + /* define SIGSEGV at the end to make it the default choice if searching the + handler fails in faulthandler_fatal_error() */ + {SIGSEGV, 0, "Segmentation fault", } +}; +static const unsigned char faulthandler_nsignals = \ + sizeof(faulthandler_handlers) / sizeof(faulthandler_handlers[0]); + +#ifdef HAVE_SIGALTSTACK +static stack_t stack; +#endif + + +/* Get the file descriptor of a file by calling its fileno() method and then + call its flush() method. + + If file is NULL or Py_None, use sys.stderr as the new file. + + On success, return the new file and write the file descriptor into *p_fd. + On error, return NULL. */ + +static PyObject* +faulthandler_get_fileno(PyObject *file, int *p_fd) +{ + PyObject *result; + long fd_long; + int fd; + + if (file == NULL || file == Py_None) { + file = PySys_GetObject("stderr"); + if (file == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr"); + return NULL; + } + } + + result = PyObject_CallMethod(file, "fileno", ""); + if (result == NULL) + return NULL; + + fd = -1; + if (PyLong_Check(result)) { + fd_long = PyLong_AsLong(result); + if (0 <= fd_long && fd_long < INT_MAX) + fd = (int)fd_long; + } + Py_DECREF(result); + + if (fd == -1) { + PyErr_SetString(PyExc_RuntimeError, + "file.fileno() is not a valid file descriptor"); + return NULL; + } + + result = PyObject_CallMethod(file, "flush", ""); + if (result != NULL) + Py_DECREF(result); + else { + /* ignore flush() error */ + PyErr_Clear(); + } + *p_fd = fd; + return file; +} + +static PyObject* +faulthandler_dump_traceback_py(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 0; + PyThreadState *tstate; + const char *errmsg; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:dump_traceback", kwlist, + &file, &all_threads)) + return NULL; + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + /* The caller holds the GIL and so PyThreadState_Get() can be used */ + tstate = PyThreadState_Get(); + if (tstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "unable to get the current thread state"); + return NULL; + } + + if (all_threads) { + errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + if (errmsg != NULL) { + PyErr_SetString(PyExc_RuntimeError, errmsg); + return NULL; + } + } + else { + _Py_DumpTraceback(fd, tstate); + } + Py_RETURN_NONE; +} + + +/* Handler of SIGSEGV, SIGFPE, SIGBUS and SIGILL signals. + + Display the current Python traceback, restore the previous handler and call + the previous handler. + + On Windows, don't call explictly the previous handler, because Windows + signal handler would not be called (for an unknown reason). The execution of + the program continues at faulthandler_fatal_error() exit, but the same + instruction will raise the same fault (signal), and so the previous handler + will be called. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_fatal_error( + int signum +#ifdef HAVE_SIGACTION + , siginfo_t *siginfo, void *ucontext +#endif +) +{ + const int fd = fatal_error.fd; + unsigned int i; + fault_handler_t *handler = NULL; + PyThreadState *tstate; + + if (!fatal_error.enabled) + return; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + if (handler->signum == signum) + break; + } + if (handler == NULL) { + /* faulthandler_nsignals == 0 (unlikely) */ + return; + } + + /* restore the previous handler */ +#ifdef HAVE_SIGACTION + (void)sigaction(handler->signum, &handler->previous, NULL); +#else + (void)signal(handler->signum, handler->previous); +#endif + handler->enabled = 0; + + PUTS(fd, "Fatal Python error: "); + PUTS(fd, handler->name); + PUTS(fd, "\n\n"); + + /* SIGSEGV, SIGFPE, SIGBUS and SIGILL are synchronous signals and so are + delivered to the thread that caused the fault. Get the Python thread + state of the current thread. + + PyThreadState_Get() doesn't give the state of the thread that caused the + fault if the thread released the GIL, and so this function cannot be + used. Read the thread local storage (TLS) instead: call + PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) + return; + + if (fatal_error.all_threads) + _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + else + _Py_DumpTraceback(fd, tstate); + +#ifndef MS_WINDOWS + /* call the previous signal handler: it is called if we use sigaction() + thanks to SA_NODEFER flag, otherwise it is deferred */ + raise(signum); +#else + /* on Windows, don't call explictly the previous handler, because Windows + signal handler would not be called */ +#endif +} + +/* Install handler for fatal signals (SIGSEGV, SIGFPE, ...). */ + +static PyObject* +faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 0; + unsigned int i; + fault_handler_t *handler; +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + int err; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:enable", kwlist, &file, &all_threads)) + return NULL; + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + Py_XDECREF(fatal_error.file); + Py_INCREF(file); + fatal_error.file = file; + fatal_error.fd = fd; + fatal_error.all_threads = all_threads; + + if (!fatal_error.enabled) { + fatal_error.enabled = 1; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; +#ifdef HAVE_SIGACTION + action.sa_sigaction = faulthandler_fatal_error; + sigemptyset(&action.sa_mask); + /* Do not prevent the signal from being received from within + its own signal handler */ + action.sa_flags = SA_NODEFER; +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + /* Call the signal handler on an alternate signal stack + provided by sigaltstack() */ + action.sa_flags |= SA_ONSTACK; + } +#endif + err = sigaction(handler->signum, &action, &handler->previous); +#else + handler->previous = signal(handler->signum, + faulthandler_fatal_error); + err = (handler->previous == SIG_ERR); +#endif + if (err) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + handler->enabled = 1; + } + } + Py_RETURN_NONE; +} + +static void +faulthandler_disable(void) +{ + unsigned int i; + fault_handler_t *handler; + + if (fatal_error.enabled) { + fatal_error.enabled = 0; + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + if (!handler->enabled) + continue; +#ifdef HAVE_SIGACTION + (void)sigaction(handler->signum, &handler->previous, NULL); +#else + (void)signal(handler->signum, handler->previous); +#endif + handler->enabled = 0; + } + } + + Py_CLEAR(fatal_error.file); +} + +static PyObject* +faulthandler_disable_py(PyObject *self) +{ + if (!fatal_error.enabled) { + Py_INCREF(Py_False); + return Py_False; + } + faulthandler_disable(); + Py_INCREF(Py_True); + return Py_True; +} + +static PyObject* +faulthandler_is_enabled(PyObject *self) +{ + return PyBool_FromLong(fatal_error.enabled); +} + +#ifdef FAULTHANDLER_LATER + +static void +faulthandler_thread(void *unused) +{ + PyLockStatus st; + const char* errmsg; + PyThreadState *current; + int ok; + + do { + st = PyThread_acquire_lock_timed(thread.cancel_event, + thread.timeout_ms, 0); + if (st == PY_LOCK_ACQUIRED) { + /* Cancelled by user */ + PyThread_release_lock(thread.cancel_event); + break; + } + /* Timeout => dump traceback */ + assert(st == PY_LOCK_FAILURE); + + /* get the thread holding the GIL, NULL if no thread hold the GIL */ + current = _Py_atomic_load_relaxed(&_PyThreadState_Current); + + errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current); + ok = (errmsg == NULL); + + if (thread.exit) + _exit(1); + } while (ok && thread.repeat); + + /* The only way out */ + thread.running = 0; + PyThread_release_lock(thread.join_event); +} + +static void +faulthandler_cancel_dump_tracebacks_later(void) +{ + if (thread.running) { + /* Notify cancellation */ + PyThread_release_lock(thread.cancel_event); + /* Wait for thread to join */ + PyThread_acquire_lock(thread.join_event, 1); + assert(thread.running == 0); + PyThread_release_lock(thread.join_event); + } + Py_CLEAR(thread.file); +} + +static PyObject* +faulthandler_dump_traceback_later(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL}; + double timeout; + PY_TIMEOUT_T timeout_ms; + int repeat = 0; + PyObject *file = NULL; + int fd; + int exit = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "d|iOi:dump_tracebacks_later", kwlist, + &timeout, &repeat, &file, &exit)) + return NULL; + timeout *= 1e6; + if (timeout >= (double) PY_TIMEOUT_MAX) { + PyErr_SetString(PyExc_OverflowError, "timeout value is too large"); + return NULL; + } + timeout_ms = (PY_TIMEOUT_T)timeout; + if (timeout_ms <= 0) { + PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0"); + return NULL; + } + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + /* Cancel previous thread, if running */ + faulthandler_cancel_dump_tracebacks_later(); + + Py_XDECREF(thread.file); + Py_INCREF(file); + thread.file = file; + thread.fd = fd; + thread.timeout_ms = timeout_ms; + thread.repeat = repeat; + thread.interp = PyThreadState_Get()->interp; + thread.exit = exit; + + /* Arm these locks to serve as events when released */ + PyThread_acquire_lock(thread.join_event, 1); + PyThread_acquire_lock(thread.cancel_event, 1); + + thread.running = 1; + if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) { + thread.running = 0; + Py_CLEAR(thread.file); + PyErr_SetString(PyExc_RuntimeError, + "unable to start watchdog thread"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject* +faulthandler_cancel_dump_tracebacks_later_py(PyObject *self) +{ + faulthandler_cancel_dump_tracebacks_later(); + Py_RETURN_NONE; +} +#endif /* FAULTHANDLER_LATER */ + +#ifdef FAULTHANDLER_USER +/* Handler of user signals (e.g. SIGUSR1). + + Dump the traceback of the current thread, or of all threads if + thread.all_threads is true. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_user(int signum) +{ + user_signal_t *user; + PyThreadState *tstate; + + user = &user_signals[signum]; + if (!user->enabled) + return; + + /* PyThreadState_Get() doesn't give the state of the current thread if + the thread doesn't hold the GIL. Read the thread local storage (TLS) + instead: call PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); + if (tstate == NULL) { + /* unable to get the current thread, do nothing */ + return; + } + + if (user->all_threads) + _Py_DumpTracebackThreads(user->fd, tstate->interp, tstate); + else + _Py_DumpTraceback(user->fd, tstate); +} + +static PyObject* +faulthandler_register(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"signum", "file", "all_threads", NULL}; + int signum; + PyObject *file = NULL; + int all_threads = 0; + int fd; + unsigned int i; + user_signal_t *user; + _Py_sighandler_t previous; +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + int err; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "i|Oi:register", kwlist, + &signum, &file, &all_threads)) + return NULL; + + if (signum < 1 || NSIG <= signum) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } + + for (i=0; i < faulthandler_nsignals; i++) { + if (faulthandler_handlers[i].signum == signum) { + PyErr_Format(PyExc_RuntimeError, + "signal %i cannot be registered by register(), " + "use enable() instead", + signum); + return NULL; + } + } + + file = faulthandler_get_fileno(file, &fd); + if (file == NULL) + return NULL; + + if (user_signals == NULL) { + user_signals = calloc(NSIG, sizeof(user_signal_t)); + if (user_signals == NULL) + return PyErr_NoMemory(); + } + user = &user_signals[signum]; + + if (!user->enabled) { +#ifdef HAVE_SIGACTION + action.sa_handler = faulthandler_user; + sigemptyset(&action.sa_mask); + /* if the signal is received while the kernel is executing a system + call, try to restart the system call instead of interrupting it and + return EINTR */ + action.sa_flags = SA_RESTART; +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + /* Call the signal handler on an alternate signal stack + provided by sigaltstack() */ + action.sa_flags |= SA_ONSTACK; + } +#endif + err = sigaction(signum, &action, &previous); +#else + previous = signal(signum, faulthandler_user); + err = (previous == SIG_ERR); +#endif + if (err) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + } + + Py_XDECREF(user->file); + Py_INCREF(file); + user->file = file; + user->fd = fd; + user->all_threads = all_threads; + user->previous = previous; + user->enabled = 1; + + Py_RETURN_NONE; +} + +static int +faulthandler_unregister(user_signal_t *user, int signum) +{ + if (user->enabled) + return 0; + user->enabled = 0; +#ifdef HAVE_SIGACTION + (void)sigaction(signum, &user->previous, NULL); +#else + (void)signal(signum, user->previous); +#endif + Py_CLEAR(user->file); + user->fd = -1; + return 1; +} + +static PyObject* +faulthandler_unregister_py(PyObject *self, PyObject *args) +{ + int signum; + user_signal_t *user; + int change; + + if (!PyArg_ParseTuple(args, "i:unregister", &signum)) + return NULL; + + if (signum < 1 || NSIG <= signum) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } + + user = &user_signals[signum]; + change = faulthandler_unregister(user, signum); + return PyBool_FromLong(change); +} +#endif /* FAULTHANDLER_USER */ + + +static PyObject * +faulthandler_read_null(PyObject *self, PyObject *args) +{ + int *x = NULL, y; + int release_gil = 0; + if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil)) + return NULL; + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + y = *x; + Py_END_ALLOW_THREADS + } else + y = *x; + return PyLong_FromLong(y); + +} + +static PyObject * +faulthandler_sigsegv(PyObject *self, PyObject *args) +{ +#if defined(MS_WINDOWS) + /* faulthandler_fatal_error() restores the previous signal handler and then + gives back the execution flow to the program. In a normal case, the + SIGSEGV was raised by the kernel because of a fault, and so if the + program retries to execute the same instruction, the fault will be + raised again. + + Here the fault is simulated by a fake SIGSEGV signal raised by the + application. We have to raise SIGSEGV at lease twice: once for + faulthandler_fatal_error(), and one more time for the previous signal + handler. */ + while(1) + raise(SIGSEGV); +#else + raise(SIGSEGV); +#endif + Py_RETURN_NONE; +} + +static PyObject * +faulthandler_sigfpe(PyObject *self, PyObject *args) +{ + /* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on + PowerPC. Use volatile to disable compile-time optimizations. */ + volatile int x = 1, y = 0, z; + z = x / y; + /* if the division by zero didn't raise a SIGFPE, raise it manually */ + raise(SIGFPE); + Py_RETURN_NONE; +} + +#ifdef SIGBUS +static PyObject * +faulthandler_sigbus(PyObject *self, PyObject *args) +{ + raise(SIGBUS); + Py_RETURN_NONE; +} +#endif + +#ifdef SIGILL +static PyObject * +faulthandler_sigill(PyObject *self, PyObject *args) +{ +#if defined(MS_WINDOWS) + /* see faulthandler_sigsegv() for the explanation about while(1) */ + while(1) + raise(SIGILL); +#else + raise(SIGILL); +#endif + Py_RETURN_NONE; +} +#endif + +static PyObject * +faulthandler_fatal_error_py(PyObject *self, PyObject *args) +{ + char *message; + if (!PyArg_ParseTuple(args, "y:fatal_error", &message)) + return NULL; + Py_FatalError(message); + Py_RETURN_NONE; +} + +#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) +void* +stack_overflow(void *min_sp, void *max_sp, size_t *depth) +{ + /* allocate 4096 bytes on the stack at each call */ + unsigned char buffer[4096]; + void *sp = &buffer; + *depth += 1; + if (sp < min_sp || max_sp < sp) + return sp; + buffer[0] = 1; + buffer[4095] = 0; + return stack_overflow(min_sp, max_sp, depth); +} + +static PyObject * +faulthandler_stack_overflow(PyObject *self) +{ + size_t depth, size; + void *sp = &depth, *stop; + + depth = 0; + stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE, + sp + STACK_OVERFLOW_MAX_SIZE, + &depth); + if (sp < stop) + size = stop - sp; + else + size = sp - stop; + PyErr_Format(PyExc_RuntimeError, + "unable to raise a stack overflow (allocated %zu bytes " + "on the stack, %zu recursive calls)", + size, depth); + return NULL; +} +#endif + + +static int +faulthandler_traverse(PyObject *module, visitproc visit, void *arg) +{ +#ifdef FAULTHANDLER_USER + unsigned int index; +#endif + +#ifdef FAULTHANDLER_LATER + Py_VISIT(thread.file); +#endif +#ifdef FAULTHANDLER_USER + if (user_signals != NULL) { + for (index=0; index < NSIG; index++) + Py_VISIT(user_signals[index].file); + } +#endif + Py_VISIT(fatal_error.file); + return 0; +} + +PyDoc_STRVAR(module_doc, +"faulthandler module."); + +static PyMethodDef module_methods[] = { + {"enable", + (PyCFunction)faulthandler_enable, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("enable(file=sys.stderr, all_threads=False): " + "enable the fault handler")}, + {"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS, + PyDoc_STR("disable(): disable the fault handler")}, + {"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS, + PyDoc_STR("is_enabled()->bool: check if the handler is enabled")}, + {"dump_traceback", + (PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=False): " + "dump the traceback of the current thread, or of all threads " + "if all_threads is True, into file")}, +#ifdef FAULTHANDLER_LATER + {"dump_tracebacks_later", + (PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_tracebacks_later(timeout, repeat=False, file=sys.stderr):\n" + "dump the traceback of all threads in timeout seconds,\n" + "or each timeout seconds if repeat is True.")}, + {"cancel_dump_tracebacks_later", + (PyCFunction)faulthandler_cancel_dump_tracebacks_later_py, METH_NOARGS, + PyDoc_STR("cancel_dump_tracebacks_later():\ncancel the previous call " + "to dump_tracebacks_later().")}, +#endif + +#ifdef FAULTHANDLER_USER + {"register", + (PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("register(signum, file=sys.stderr, all_threads=False): " + "register an handler for the signal 'signum': dump the " + "traceback of the current thread, or of all threads if " + "all_threads is True, into file")}, + {"unregister", + faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("unregister(signum): unregister the handler of the signal " + "'signum' registered by register()")}, +#endif + + {"_read_null", faulthandler_read_null, METH_VARARGS, + PyDoc_STR("_read_null(release_gil=False): read from NULL, raise " + "a SIGSEGV or SIGBUS signal depending on the platform")}, + {"_sigsegv", faulthandler_sigsegv, METH_VARARGS, + PyDoc_STR("_sigsegv(): raise a SIGSEGV signal")}, + {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS, + PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, +#ifdef SIGBUS + {"_sigbus", (PyCFunction)faulthandler_sigbus, METH_NOARGS, + PyDoc_STR("_sigbus(): raise a SIGBUS signal")}, +#endif +#ifdef SIGILL + {"_sigill", (PyCFunction)faulthandler_sigill, METH_NOARGS, + PyDoc_STR("_sigill(): raise a SIGILL signal")}, +#endif + {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, + PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, +#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) + {"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS, + PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")}, +#endif + {NULL, NULL} /* terminator */ +}; + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "faulthandler", + module_doc, + 0, /* non negative size to be able to unload the module */ + module_methods, + NULL, + faulthandler_traverse, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_faulthandler(void) +{ + return PyModule_Create(&module_def); +} + +/* Call faulthandler.enable() if PYTHONFAULTHANDLER environment variable is + defined, or if sys._xoptions has a 'faulthandler' key. */ + +static int +faulthandler_env_options(void) +{ + PyObject *xoptions, *key, *module, *res; + int enable; + + if (!Py_GETENV("PYTHONFAULTHANDLER")) { + xoptions = PySys_GetXOptions(); + if (xoptions == NULL) + return -1; + + key = PyUnicode_FromString("faulthandler"); + if (key == NULL) + return -1; + + enable = PyDict_Contains(xoptions, key); + Py_DECREF(key); + if (!enable) + return 0; + } + else + enable = 1; + + module = PyImport_ImportModule("faulthandler"); + if (module == NULL) { + return -1; + } + res = PyObject_CallMethod(module, "enable", ""); + Py_DECREF(module); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +int _PyFaulthandler_Init(void) +{ +#ifdef HAVE_SIGALTSTACK + int err; + + /* Try to allocate an alternate stack for faulthandler() signal handler to + * be able to allocate memory on the stack, even on a stack overflow. If it + * fails, ignore the error. */ + stack.ss_flags = 0; + stack.ss_size = SIGSTKSZ; + stack.ss_sp = PyMem_Malloc(stack.ss_size); + if (stack.ss_sp != NULL) { + err = sigaltstack(&stack, NULL); + if (err) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } + } +#endif +#ifdef FAULTHANDLER_LATER + thread.running = 0; + thread.file = NULL; + thread.cancel_event = PyThread_allocate_lock(); + thread.join_event = PyThread_allocate_lock(); + if (!thread.cancel_event || !thread.join_event) { + PyErr_SetString(PyExc_RuntimeError, + "could not allocate locks for faulthandler"); + return -1; + } +#endif + + return faulthandler_env_options(); +} + +void _PyFaulthandler_Fini(void) +{ +#ifdef FAULTHANDLER_USER + unsigned int i; +#endif + +#ifdef FAULTHANDLER_LATER + /* later */ + faulthandler_cancel_dump_tracebacks_later(); + if (thread.cancel_event) { + PyThread_free_lock(thread.cancel_event); + thread.cancel_event = NULL; + } + if (thread.join_event) { + PyThread_free_lock(thread.join_event); + thread.join_event = NULL; + } +#endif + +#ifdef FAULTHANDLER_USER + /* user */ + if (user_signals != NULL) { + for (i=0; i < NSIG; i++) + faulthandler_unregister(&user_signals[i], i+1); + free(user_signals); + user_signals = NULL; + } +#endif + + /* fatal */ + faulthandler_disable(); +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } +#endif +} diff --git a/src/main/resource/testFiles/cpython/revFiles/58fb90_c9e435_Modules#_stringio.c b/src/main/resource/testFiles/cpython/revFiles/58fb90_c9e435_Modules#_stringio.c new file mode 100644 index 0000000..f342048 --- /dev/null +++ b/src/main/resource/testFiles/cpython/revFiles/58fb90_c9e435_Modules#_stringio.c @@ -0,0 +1,383 @@ +#include "Python.h" + +/* This module is a stripped down version of _bytesio.c with a Py_UNICODE + buffer. Most of the functionality is provided by subclassing _StringIO. */ + + +typedef struct { + PyObject_HEAD + Py_UNICODE *buf; + Py_ssize_t pos; + Py_ssize_t string_size; + size_t buf_size; +} StringIOObject; + + +/* Internal routine for changing the size, in terms of characters, of the + buffer of StringIO objects. The caller should ensure that the 'size' + argument is non-negative. Returns 0 on success, -1 otherwise. */ +static int +resize_buffer(StringIOObject *self, size_t size) +{ + /* Here, unsigned types are used to avoid dealing with signed integer + overflow, which is undefined in C. */ + size_t alloc = self->buf_size; + Py_UNICODE *new_buf = NULL; + + assert(self->buf != NULL); + + /* For simplicity, stay in the range of the signed type. Anyway, Python + doesn't allow strings to be longer than this. */ + if (size > PY_SSIZE_T_MAX) + goto overflow; + + if (size < alloc / 2) { + /* Major downsize; resize down to exact size. */ + alloc = size + 1; + } + else if (size < alloc) { + /* Within allocated size; quick exit */ + return 0; + } + else if (size <= alloc * 1.125) { + /* Moderate upsize; overallocate similar to list_resize() */ + alloc = size + (size >> 3) + (size < 9 ? 3 : 6); + } + else { + /* Major upsize; resize up to exact size */ + alloc = size + 1; + } + + if (alloc > ((size_t)-1) / sizeof(Py_UNICODE)) + goto overflow; + new_buf = (Py_UNICODE *)PyMem_Realloc(self->buf, + alloc * sizeof(Py_UNICODE)); + if (new_buf == NULL) { + PyErr_NoMemory(); + return -1; + } + self->buf_size = alloc; + self->buf = new_buf; + + return 0; + + overflow: + PyErr_SetString(PyExc_OverflowError, + "new buffer size too large"); + return -1; +} + +/* Internal routine for writing a string of characters to the buffer of a + StringIO object. Returns the number of bytes wrote, or -1 on error. */ +static Py_ssize_t +write_str(StringIOObject *self, const Py_UNICODE *str, Py_ssize_t len) +{ + assert(self->buf != NULL); + assert(self->pos >= 0); + assert(len >= 0); + + /* This overflow check is not strictly necessary. However, it avoids us to + deal with funky things like comparing an unsigned and a signed + integer. */ + if (self->pos > PY_SSIZE_T_MAX - len) { + PyErr_SetString(PyExc_OverflowError, + "new position too large"); + return -1; + } + if (self->pos + len > self->string_size) { + if (resize_buffer(self, self->pos + len) < 0) + return -1; + } + + if (self->pos > self->string_size) { + /* In case of overseek, pad with null bytes the buffer region between + the end of stream and the current position. + + 0 lo string_size hi + | |<---used--->|<----------available----------->| + | | <--to pad-->|<---to write---> | + 0 buf positon + + */ + memset(self->buf + self->string_size, '\0', + (self->pos - self->string_size) * sizeof(Py_UNICODE)); + } + + /* Copy the data to the internal buffer, overwriting some of the + existing data if self->pos < self->string_size. */ + memcpy(self->buf + self->pos, str, len * sizeof(Py_UNICODE)); + self->pos += len; + + /* Set the new length of the internal string if it has changed */ + if (self->string_size < self->pos) { + self->string_size = self->pos; + } + + return len; +} + +static PyObject * +stringio_getvalue(StringIOObject *self) +{ + return PyUnicode_FromUnicode(self->buf, self->string_size); +} + +static PyObject * +stringio_tell(StringIOObject *self) +{ + return PyLong_FromSsize_t(self->pos); +} + +static PyObject * +stringio_read(StringIOObject *self, PyObject *args) +{ + Py_ssize_t size, n; + Py_UNICODE *output; + PyObject *arg = Py_None; + + if (!PyArg_ParseTuple(args, "|O:read", &arg)) + return NULL; + + if (PyLong_Check(arg)) { + size = PyLong_AsSsize_t(arg); + if (size == -1 && PyErr_Occurred()) + return NULL; + } + else if (arg == Py_None) { + /* Read until EOF is reached, by default. */ + size = -1; + } + else { + PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", + Py_TYPE(arg)->tp_name); + return NULL; + } + + /* adjust invalid sizes */ + n = self->string_size - self->pos; + if (size < 0 || size > n) { + size = n; + if (size < 0) + size = 0; + } + + assert(self->buf != NULL); + output = self->buf + self->pos; + self->pos += size; + + return PyUnicode_FromUnicode(output, size); +} + +static PyObject * +stringio_truncate(StringIOObject *self, PyObject *args) +{ + Py_ssize_t size; + PyObject *arg = Py_None; + + if (!PyArg_ParseTuple(args, "|O:truncate", &arg)) + return NULL; + + if (PyLong_Check(arg)) { + size = PyLong_AsSsize_t(arg); + if (size == -1 && PyErr_Occurred()) + return NULL; + } + else if (arg == Py_None) { + /* Truncate to current position if no argument is passed. */ + size = self->pos; + } + else { + PyErr_Format(PyExc_TypeError, "integer argument expected, got '%s'", + Py_TYPE(arg)->tp_name); + return NULL; + } + + if (size < 0) { + PyErr_Format(PyExc_ValueError, + "Negative size value %zd", size); + return NULL; + } + + if (size < self->string_size) { + self->string_size = size; + if (resize_buffer(self, size) < 0) + return NULL; + } + self->pos = size; + + return PyLong_FromSsize_t(size); +} + +static PyObject * +stringio_seek(StringIOObject *self, PyObject *args) +{ + Py_ssize_t pos; + int mode = 0; + + if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode)) + return NULL; + + if (mode != 0 && mode != 1 && mode != 2) { + PyErr_Format(PyExc_ValueError, + "Invalid whence (%i, should be 0, 1 or 2)", mode); + return NULL; + } + else if (pos < 0 && mode == 0) { + PyErr_Format(PyExc_ValueError, + "Negative seek position %zd", pos); + return NULL; + } + else if (mode != 0 && pos != 0) { + PyErr_SetString(PyExc_IOError, + "Can't do nonzero cur-relative seeks"); + return NULL; + } + + /* mode 0: offset relative to beginning of the string. + mode 1: no change to current position. + mode 2: change position to end of file. */ + if (mode == 1) { + pos = self->pos; + } + else if (mode == 2) { + pos = self->string_size; + } + + self->pos = pos; + + return PyLong_FromSsize_t(self->pos); +} + +static PyObject * +stringio_write(StringIOObject *self, PyObject *obj) +{ + const Py_UNICODE *str; + Py_ssize_t size; + Py_ssize_t n = 0; + + if (PyUnicode_Check(obj)) { + str = PyUnicode_AsUnicode(obj); + size = PyUnicode_GetSize(obj); + } + else { + PyErr_Format(PyExc_TypeError, "string argument expected, got '%s'", + Py_TYPE(obj)->tp_name); + return NULL; + } + + if (size != 0) { + n = write_str(self, str, size); + if (n < 0) + return NULL; + } + + return PyLong_FromSsize_t(n); +} + +static void +stringio_dealloc(StringIOObject *self) +{ + PyMem_Free(self->buf); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +stringio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + StringIOObject *self; + + assert(type != NULL && type->tp_alloc != NULL); + self = (StringIOObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + self->string_size = 0; + self->pos = 0; + self->buf_size = 0; + self->buf = (Py_UNICODE *)PyMem_Malloc(0); + if (self->buf == NULL) { + Py_DECREF(self); + return PyErr_NoMemory(); + } + + return (PyObject *)self; +} + +static struct PyMethodDef stringio_methods[] = { + {"getvalue", (PyCFunction)stringio_getvalue, METH_VARARGS, NULL}, + {"read", (PyCFunction)stringio_read, METH_VARARGS, NULL}, + {"tell", (PyCFunction)stringio_tell, METH_NOARGS, NULL}, + {"truncate", (PyCFunction)stringio_truncate, METH_VARARGS, NULL}, + {"seek", (PyCFunction)stringio_seek, METH_VARARGS, NULL}, + {"write", (PyCFunction)stringio_write, METH_O, NULL}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject StringIO_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_stringio._StringIO", /*tp_name*/ + sizeof(StringIOObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)stringio_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + stringio_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + stringio_new, /*tp_new*/ +}; + +static struct PyModuleDef _stringiomodule = { + PyModuleDef_HEAD_INIT, + "_stringio", + NULL, + -1, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__stringio(void) +{ + PyObject *m; + + if (PyType_Ready(&StringIO_Type) < 0) + return NULL; + m = PyModule_Create(&_stringiomodule); + if (m == NULL) + return NULL; + Py_INCREF(&StringIO_Type); + if (PyModule_AddObject(m, "_StringIO", (PyObject *)&StringIO_Type) < 0) + return NULL; + return m; +} diff --git a/src/main/resource/testFiles/lighttpd1.4/prevFiles/prev_f27b8d_4a86d3_src#connections.c b/src/main/resource/testFiles/lighttpd1.4/prevFiles/prev_f27b8d_4a86d3_src#connections.c new file mode 100644 index 0000000..cdb8c94 --- /dev/null +++ b/src/main/resource/testFiles/lighttpd1.4/prevFiles/prev_f27b8d_4a86d3_src#connections.c @@ -0,0 +1,1707 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "server.h" +#include "log.h" +#include "connections.h" +#include "fdevent.h" + +#include "request.h" +#include "response.h" +#include "network.h" +#include "http_chunk.h" +#include "stat_cache.h" +#include "joblist.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + +#ifdef USE_OPENSSL +# include +# include +#endif + +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#include "sys-socket.h" + +typedef struct { + PLUGIN_DATA; +} plugin_data; + +static connection *connections_get_new_connection(server *srv) { + connections *conns = srv->conns; + size_t i; + + if (conns->size == 0) { + conns->size = 128; + conns->ptr = NULL; + conns->ptr = malloc(sizeof(*conns->ptr) * conns->size); + for (i = 0; i < conns->size; i++) { + conns->ptr[i] = connection_init(srv); + } + } else if (conns->size == conns->used) { + conns->size += 128; + conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size); + + for (i = conns->used; i < conns->size; i++) { + conns->ptr[i] = connection_init(srv); + } + } + + connection_reset(srv, conns->ptr[conns->used]); +#if 0 + fprintf(stderr, "%s.%d: add: ", __FILE__, __LINE__); + for (i = 0; i < conns->used + 1; i++) { + fprintf(stderr, "%d ", conns->ptr[i]->fd); + } + fprintf(stderr, "\n"); +#endif + + conns->ptr[conns->used]->ndx = conns->used; + return conns->ptr[conns->used++]; +} + +static int connection_del(server *srv, connection *con) { + size_t i; + connections *conns = srv->conns; + connection *temp; + + if (con == NULL) return -1; + + if (-1 == con->ndx) return -1; + + i = con->ndx; + + /* not last element */ + + if (i != conns->used - 1) { + temp = conns->ptr[i]; + conns->ptr[i] = conns->ptr[conns->used - 1]; + conns->ptr[conns->used - 1] = temp; + + conns->ptr[i]->ndx = i; + conns->ptr[conns->used - 1]->ndx = -1; + } + + conns->used--; + + con->ndx = -1; +#if 0 + fprintf(stderr, "%s.%d: del: (%d)", __FILE__, __LINE__, conns->used); + for (i = 0; i < conns->used; i++) { + fprintf(stderr, "%d ", conns->ptr[i]->fd); + } + fprintf(stderr, "\n"); +#endif + return 0; +} + +int connection_close(server *srv, connection *con) { +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + if (con->ssl) SSL_free(con->ssl); + con->ssl = NULL; + } +#endif + + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + fdevent_unregister(srv->ev, con->fd); +#ifdef __WIN32 + if (closesocket(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#else + if (close(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#endif + + srv->cur_fds--; +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "closed()", con->fd); +#endif + + connection_del(srv, con); + connection_set_state(srv, con, CON_STATE_CONNECT); + + return 0; +} + +#if 0 +static void dump_packet(const unsigned char *data, size_t len) { + size_t i, j; + + if (len == 0) return; + + for (i = 0; i < len; i++) { + if (i % 16 == 0) fprintf(stderr, " "); + + fprintf(stderr, "%02x ", data[i]); + + if ((i + 1) % 16 == 0) { + fprintf(stderr, " "); + for (j = 0; j <= i % 16; j++) { + unsigned char c; + + if (i-15+j >= len) break; + + c = data[i-15+j]; + + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + + fprintf(stderr, "\n"); + } + } + + if (len % 16 != 0) { + for (j = i % 16; j < 16; j++) { + fprintf(stderr, " "); + } + + fprintf(stderr, " "); + for (j = i & ~0xf; j < len; j++) { + unsigned char c; + + c = data[j]; + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + fprintf(stderr, "\n"); + } +} +#endif + +static int connection_handle_read(server *srv, connection *con) { + int len; + buffer *b; + int toread; +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + + b = chunkqueue_get_append_buffer(con->read_queue); + buffer_prepare_copy(b, 4096); + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + len = SSL_read(con->ssl, b->ptr, b->size - 1); + } else { + if (ioctl(con->fd, FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "unexpected end-of-file:", + con->fd); + return -1; + } + buffer_prepare_copy(b, toread); + + len = read(con->fd, b->ptr, b->size - 1); + } +#elif defined(__WIN32) + len = recv(con->fd, b->ptr, b->size - 1, 0); +#else + if (ioctl(con->fd, FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "unexpected end-of-file:", + con->fd); + return -1; + } + buffer_prepare_copy(b, toread); + + len = read(con->fd, b->ptr, b->size - 1); +#endif + + if (len < 0) { + con->is_readable = 0; + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + int r, ssl_err; + + switch ((r = SSL_get_error(con->ssl, len))) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_SYSCALL: + /** + * man SSL_get_error() + * + * SSL_ERROR_SYSCALL + * Some I/O error occurred. The OpenSSL error queue may contain more + * information on the error. If the error queue is empty (i.e. + * ERR_get_error() returns 0), ret can be used to find out more about + * the error: If ret == 0, an EOF was observed that violates the + * protocol. If ret == -1, the underlying BIO reported an I/O error + * (for socket I/O on Unix systems, consult errno for details). + * + */ + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + + switch(errno) { + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + len, r, errno, + strerror(errno)); + break; + } + + break; + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) { + /* FIXME: later */ + } + + /* fall thourgh */ + default: + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + break; + } + } else { + if (errno == EAGAIN) return 0; + if (errno == EINTR) { + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + } + + if (errno != ECONNRESET) { + /* expected for keep-alive */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + } + } +#else + if (errno == EAGAIN) return 0; + if (errno == EINTR) { + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + } + + if (errno != ECONNRESET) { + /* expected for keep-alive */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + } +#endif + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + /* pipelining */ + + return -2; + } else if ((size_t)len < b->size - 1) { + /* we got less then expected, wait for the next fd-event */ + + con->is_readable = 0; + } + + b->used = len; + b->ptr[b->used++] = '\0'; + + con->bytes_read += len; +#if 0 + dump_packet(b->ptr, len); +#endif + + return 0; +} + +static int connection_handle_write_prepare(server *srv, connection *con) { + if (con->mode == DIRECT) { + /* static files */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + case HTTP_METHOD_PUT: + case HTTP_METHOD_MKCOL: + case HTTP_METHOD_DELETE: + case HTTP_METHOD_COPY: + case HTTP_METHOD_MOVE: + case HTTP_METHOD_PROPFIND: + case HTTP_METHOD_PROPPATCH: + break; + case HTTP_METHOD_OPTIONS: + /* + * 400 is coming from the request-parser BEFORE uri.path is set + * 403 is from the response handler when noone else catched it + * + * */ + if (con->uri.path->used && + con->uri.path->ptr[0] != '*') { + response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + + con->http_status = 200; + con->file_finished = 1; + + chunkqueue_reset(con->write_queue); + } + break; + default: + switch(con->http_status) { + case 400: /* bad request */ + case 505: /* unknown protocol */ + case 207: /* this was webdav */ + break; + default: + con->http_status = 501; + break; + } + break; + } + } + + if (con->http_status == 0) { + con->http_status = 403; + } + + switch(con->http_status) { + case 400: /* class: header + custom body */ + case 401: + case 403: + case 404: + case 408: + case 411: + case 416: + case 500: + case 501: + case 503: + case 505: + if (con->mode != DIRECT) break; + + con->file_finished = 0; + + buffer_reset(con->physical.path); + + /* try to send static errorfile */ + if (!buffer_is_empty(con->conf.errorfile_prefix)) { + stat_cache_entry *sce = NULL; + + buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); + buffer_append_string(con->physical.path, get_http_status_body_name(con->http_status)); + + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + con->file_finished = 1; + + http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + } + } + + if (!con->file_finished) { + buffer *b; + + buffer_reset(con->physical.path); + + con->file_finished = 1; + b = chunkqueue_get_append_buffer(con->write_queue); + + /* build default error-page */ + buffer_copy_string(b, + "\n" + "\n" + "\n" + " \n" + " "); + buffer_append_long(b, con->http_status); + buffer_append_string(b, " - "); + buffer_append_string(b, get_http_status_name(con->http_status)); + + buffer_append_string(b, + "\n" + " \n" + " \n" + "

"); + buffer_append_long(b, con->http_status); + buffer_append_string(b, " - "); + buffer_append_string(b, get_http_status_name(con->http_status)); + + buffer_append_string(b,"

\n" + " \n" + "\n" + ); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } + /* fall through */ + case 207: + case 200: /* class: header + body */ + case 302: + break; + + case 206: /* write_queue is already prepared */ + con->file_finished = 1; + + break; + case 205: /* class: header only */ + case 301: + case 304: + default: + /* disable chunked encoding again as we have no body */ + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + chunkqueue_reset(con->write_queue); + + con->file_finished = 1; + break; + } + + + if (con->file_finished) { + /* we have all the content and chunked encoding is not used, set a content-length */ + + if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && + (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { + buffer_copy_off_t(srv->tmp_buf, chunkqueue_length(con->write_queue)); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); + } + } else { + /* disable keep-alive if size-info for the body is missing */ + if ((con->parsed_response & HTTP_CONTENT_LENGTH) && + ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { + con->keep_alive = 0; + } + + if (0 == (con->parsed_response & HTTP_CONNECTION)) { + /* (f)cgi did'nt send Connection: header + * + * shall we ? + */ + if (((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) && + (con->parsed_response & HTTP_CONTENT_LENGTH) == 0) { + /* without content_length, no keep-alive */ + + con->keep_alive = 0; + } + } else { + /* a subrequest disable keep-alive although the client wanted it */ + if (con->keep_alive && !con->response.keep_alive) { + con->keep_alive = 0; + + /* FIXME: we have to drop the Connection: Header from the subrequest */ + } + } + } + + if (con->request.http_method == HTTP_METHOD_HEAD) { + chunkqueue_reset(con->write_queue); + } + + http_response_write_header(srv, con); + + return 0; +} + +static int connection_handle_write(server *srv, connection *con) { + switch(network_write_chunkqueue(srv, con, con->write_queue)) { + case 0: + if (con->file_finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + joblist_append(srv, con); + } + break; + case -1: /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: write failed on fd", con->fd); + connection_set_state(srv, con, CON_STATE_ERROR); + joblist_append(srv, con); + break; + case -2: /* remote close */ + connection_set_state(srv, con, CON_STATE_ERROR); + joblist_append(srv, con); + break; + case 1: + con->is_writable = 0; + + /* not finished yet -> WRITE */ + break; + } + + return 0; +} + + + +connection *connection_init(server *srv) { + connection *con; + + UNUSED(srv); + + con = calloc(1, sizeof(*con)); + + con->fd = 0; + con->ndx = -1; + con->fde_ndx = -1; + con->bytes_written = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + +#define CLEAN(x) \ + con->x = buffer_init(); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.rel_path); + CLEAN(physical.etag); + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); + CLEAN(dst_addr_buf); + +#undef CLEAN + con->write_queue = chunkqueue_init(); + con->read_queue = chunkqueue_init(); + con->request_content_queue = chunkqueue_init(); + chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs); + + con->request.headers = array_init(); + con->response.headers = array_init(); + con->environment = array_init(); + + /* init plugin specific connection structures */ + + con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); + + con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); + config_setup_connection(srv, con); + + return con; +} + +void connections_free(server *srv) { + connections *conns = srv->conns; + size_t i; + + for (i = 0; i < conns->size; i++) { + connection *con = conns->ptr[i]; + + connection_reset(srv, con); + + chunkqueue_free(con->write_queue); + chunkqueue_free(con->read_queue); + chunkqueue_free(con->request_content_queue); + array_free(con->request.headers); + array_free(con->response.headers); + array_free(con->environment); + +#define CLEAN(x) \ + buffer_free(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.etag); + CLEAN(physical.rel_path); + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); + CLEAN(dst_addr_buf); +#undef CLEAN + free(con->plugin_ctx); + free(con->cond_cache); + + free(con); + } + + free(conns->ptr); +} + + +int connection_reset(server *srv, connection *con) { + size_t i; + + plugins_call_connection_reset(srv, con); + + con->is_readable = 1; + con->is_writable = 1; + con->http_status = 0; + con->file_finished = 0; + con->file_started = 0; + con->got_response = 0; + + con->parsed_response = 0; + + con->bytes_written = 0; + con->bytes_written_cur_second = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + + con->request.http_method = HTTP_METHOD_UNSET; + con->request.http_version = HTTP_VERSION_UNSET; + + con->request.http_if_modified_since = NULL; + con->request.http_if_none_match = NULL; + + con->response.keep_alive = 0; + con->response.content_length = -1; + con->response.transfer_encoding = 0; + + con->mode = DIRECT; + +#define CLEAN(x) \ + if (con->x) buffer_reset(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.pathinfo); + CLEAN(request.request); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.rel_path); + CLEAN(physical.etag); + + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); +#undef CLEAN + +#define CLEAN(x) \ + if (con->x) con->x->used = 0; + +#undef CLEAN + +#define CLEAN(x) \ + con->request.x = NULL; + + CLEAN(http_host); + CLEAN(http_range); + CLEAN(http_content_type); +#undef CLEAN + con->request.content_length = 0; + + array_reset(con->request.headers); + array_reset(con->response.headers); + array_reset(con->environment); + + chunkqueue_reset(con->write_queue); + chunkqueue_reset(con->request_content_queue); + + /* the plugins should cleanup themself */ + for (i = 0; i < srv->plugins.used; i++) { + plugin *p = ((plugin **)(srv->plugins.ptr))[i]; + plugin_data *pd = p->data; + + if (!pd) continue; + + if (con->plugin_ctx[pd->id] != NULL) { + log_error_write(srv, __FILE__, __LINE__, "sb", "missing cleanup in", p->name); + } + + con->plugin_ctx[pd->id] = NULL; + } + +#if COND_RESULT_UNSET + for (i = srv->config_context->used - 1; i >= 0; i --) { + con->cond_cache[i].result = COND_RESULT_UNSET; + con->cond_cache[i].patterncount = 0; + } +#else + memset(con->cond_cache, 0, sizeof(cond_cache_t) * srv->config_context->used); +#endif + + con->header_len = 0; + con->in_error_handler = 0; + + config_setup_connection(srv, con); + + return 0; +} + +/** + * + * search for \r\n\r\n + * + * this is a special 32bit version which is using a sliding window for + * the comparisions + * + * how it works: + * + * b: 'abcdefg' + * rnrn: 'cdef' + * + * cmpbuf: abcd != cdef + * cmpbuf: bcde != cdef + * cmpbuf: cdef == cdef -> return &c + * + * cmpbuf and rnrn are treated as 32bit uint and bit-ops are used to + * maintain cmpbuf and rnrn + * + */ + +char *buffer_search_rnrn(buffer *b) { + uint32_t cmpbuf, rnrn; + char *cp; + size_t i; + + if (b->used < 4) return NULL; + + rnrn = ('\r' << 24) | ('\n' << 16) | + ('\r' << 8) | ('\n' << 0); + + cmpbuf = (b->ptr[0] << 24) | (b->ptr[1] << 16) | + (b->ptr[2] << 8) | (b->ptr[3] << 0); + + cp = b->ptr + 4; + for (i = 0; i < b->used - 4; i++) { + if (cmpbuf == rnrn) return cp - 4; + + cmpbuf = (cmpbuf << 8 | *(cp++)) & 0xffffffff; + } + + return NULL; +} +/** + * handle all header and content read + * + * we get called by the state-engine and by the fdevent-handler + */ +int connection_handle_read_state(server *srv, connection *con) { + int ostate = con->state; + char *h_term = NULL; + chunk *c; + chunkqueue *cq = con->read_queue; + chunkqueue *dst_cq = con->request_content_queue; + + if (con->is_readable) { + con->read_idle_ts = srv->cur_ts; + + switch(connection_handle_read(srv, con)) { + case -1: + return -1; + case -2: + /* remote side closed the connection + * if we still have content, handle it, if not leave here */ + + if (cq->first == cq->last && + cq->first->mem->used == 0) { + + /* conn-closed, leave here */ + connection_set_state(srv, con, CON_STATE_ERROR); + } + default: + break; + } + } + + /* the last chunk might be empty */ + for (c = cq->first; c;) { + if (cq->first == c && c->mem->used == 0) { + /* the first node is empty */ + /* ... and it is empty, move it to unused */ + + cq->first = c->next; + if (cq->first == NULL) cq->last = NULL; + + c->next = cq->unused; + cq->unused = c; + cq->unused_chunks++; + + c = cq->first; + } else if (c->next && c->next->mem->used == 0) { + chunk *fc; + /* next node is the last one */ + /* ... and it is empty, move it to unused */ + + fc = c->next; + c->next = fc->next; + + fc->next = cq->unused; + cq->unused = fc; + cq->unused_chunks++; + + /* the last node was empty */ + if (c->next == NULL) { + cq->last = c; + } + + c = c->next; + } else { + c = c->next; + } + } + + /* nothing to handle */ + if (cq->first == NULL) return 0; + + switch(ostate) { + case CON_STATE_READ: + /* prepare con->request.request */ + c = cq->first; + + /* check if we need the full package */ + if (con->request.request->used == 0) { + buffer b; + + b.ptr = c->mem->ptr + c->offset; + b.used = c->mem->used - c->offset; + + if (NULL != (h_term = buffer_search_rnrn(&b))) { + /* \r\n\r\n found + * - copy everything incl. the terminator to request.request + */ + + buffer_copy_string_len(con->request.request, + b.ptr, + h_term - b.ptr + 4); + + /* the buffer has been read up to the terminator */ + c->offset += h_term - b.ptr + 4; + } else { + /* not found, copy everything */ + buffer_copy_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + c->offset = c->mem->used - 1; + } + } else { + /* have to take care of overlapping header terminators */ + + size_t l = con->request.request->used - 2; + char *s = con->request.request->ptr; + buffer b; + + b.ptr = c->mem->ptr + c->offset; + b.used = c->mem->used - c->offset; + + if (con->request.request->used - 1 > 3 && + c->mem->used > 1 && + s[l-2] == '\r' && + s[l-1] == '\n' && + s[l-0] == '\r' && + c->mem->ptr[0] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 1); + c->offset += 1; + + h_term = con->request.request->ptr; + } else if (con->request.request->used - 1 > 2 && + c->mem->used > 2 && + s[l-1] == '\r' && + s[l-0] == '\n' && + c->mem->ptr[0] == '\r' && + c->mem->ptr[1] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 2); + c->offset += 2; + + h_term = con->request.request->ptr; + } else if (con->request.request->used - 1 > 1 && + c->mem->used > 3 && + s[l-0] == '\r' && + c->mem->ptr[0] == '\n' && + c->mem->ptr[1] == '\r' && + c->mem->ptr[2] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 3); + c->offset += 3; + + h_term = con->request.request->ptr; + } else if (NULL != (h_term = buffer_search_string_len(&b, "\r\n\r\n", 4))) { + /* \r\n\r\n found + * - copy everything incl. the terminator to request.request + */ + + buffer_append_string_len(con->request.request, + c->mem->ptr + c->offset, + c->offset + h_term - b.ptr + 4); + + /* the buffer has been read up to the terminator */ + c->offset += h_term - b.ptr + 4; + } else { + /* not found, copy everything */ + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + c->offset = c->mem->used - 1; + } + } + + /* con->request.request is setup up */ + if (h_term) { + connection_set_state(srv, con, CON_STATE_REQUEST_END); + } else if (con->request.request->used > 64 * 1024) { + log_error_write(srv, __FILE__, __LINE__, "sd", "http-header larger then 64k -> disconnected", chunkqueue_length(cq)); + + con->http_status = 414; /* Request-URI too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_REQUEST_END); + } + break; + case CON_STATE_READ_POST: + for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) { + off_t weWant, weHave, toRead; + + weWant = con->request.content_length - dst_cq->bytes_in; + + assert(c->mem->used); + + weHave = c->mem->used - c->offset - 1; + + toRead = weHave > weWant ? weWant : weHave; + + /* the new way, copy everything into a chunkqueue whcih might use tempfiles */ + if (con->request.content_length > 64 * 1024) { + chunk *dst_c = NULL; + /* copy everything to max 1Mb sized tempfiles */ + + /* + * if the last chunk is + * - smaller than 1Mb (size < 1Mb) + * - not read yet (offset == 0) + * -> append to it + * otherwise + * -> create a new chunk + * + * */ + + if (dst_cq->last && + dst_cq->last->type == FILE_CHUNK && + dst_cq->last->file.is_temp && + dst_cq->last->offset == 0) { + /* ok, take the last chunk for our job */ + + if (dst_cq->last->file.length < 1 * 1024 * 1024) { + dst_c = dst_cq->last; + + if (dst_c->file.fd == -1) { + /* this should not happen as we cache the fd, but you never know */ + dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND); + } + } else { + /* the chunk is too large now, close it */ + dst_c = dst_cq->last; + + if (dst_c->file.fd != -1) { + close(dst_c->file.fd); + dst_c->file.fd = -1; + } + dst_c = chunkqueue_get_append_tempfile(dst_cq); + } + } else { + dst_c = chunkqueue_get_append_tempfile(dst_cq); + } + + /* we have a chunk, let's write to it */ + + if (dst_c->file.fd == -1) { + /* we don't have file to write to, + * EACCES might be one reason. + * + * Instead of sending 500 we send 413 and say the request is too large + * */ + + log_error_write(srv, __FILE__, __LINE__, "sbs", + "denying upload as opening to temp-file for upload failed:", + dst_c->file.name, strerror(errno)); + + con->http_status = 413; /* Request-Entity too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + } + + if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { + /* write failed for some reason ... disk full ? */ + log_error_write(srv, __FILE__, __LINE__, "sbs", + "denying upload as writing to file failed:", + dst_c->file.name, strerror(errno)); + + con->http_status = 413; /* Request-Entity too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + close(dst_c->file.fd); + dst_c->file.fd = -1; + + break; + } + + dst_c->file.length += toRead; + + if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) { + /* we read everything, close the chunk */ + close(dst_c->file.fd); + dst_c->file.fd = -1; + } + } else { + buffer *b; + + b = chunkqueue_get_append_buffer(dst_cq); + buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead); + } + + c->offset += toRead; + dst_cq->bytes_in += toRead; + } + + /* Content is ready */ + if (dst_cq->bytes_in == (off_t)con->request.content_length) { + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + } + + break; + } + + chunkqueue_remove_finished_chunks(cq); + + return 0; +} + +handler_t connection_handle_fdevent(void *s, void *context, int revents) { + server *srv = (server *)s; + connection *con = context; + + joblist_append(srv, con); + + if (revents & FDEVENT_IN) { + con->is_readable = 1; +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", "read-wait - done", con->fd); +#endif + } + if (revents & FDEVENT_OUT) { + con->is_writable = 1; + /* we don't need the event twice */ + } + + + if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { + /* looks like an error */ + + /* FIXME: revents = 0x19 still means that we should read from the queue */ + if (revents & FDEVENT_HUP) { + if (con->state == CON_STATE_CLOSE) { + con->close_timeout_ts = 0; + } else { + /* sigio reports the wrong event here + * + * there was no HUP at all + */ +#ifdef USE_LINUX_SIGIO + if (srv->ev->in_sigio == 1) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> HUP", con->fd); + } else { + connection_set_state(srv, con, CON_STATE_ERROR); + } +#else + connection_set_state(srv, con, CON_STATE_ERROR); +#endif + + } + } else if (revents & FDEVENT_ERR) { +#ifndef USE_LINUX_SIGIO + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> ERR", con->fd); +#endif + connection_set_state(srv, con, CON_STATE_ERROR); + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> ???", revents); + } + } + + if (con->state == CON_STATE_READ || + con->state == CON_STATE_READ_POST) { + connection_handle_read_state(srv, con); + } + + if (con->state == CON_STATE_WRITE && + !chunkqueue_is_empty(con->write_queue) && + con->is_writable) { + + if (-1 == connection_handle_write(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + } else if (con->state == CON_STATE_WRITE) { + con->write_request_ts = srv->cur_ts; + } + } + + if (con->state == CON_STATE_CLOSE) { + /* flush the read buffers */ + int b; + + if (ioctl(con->fd, FIONREAD, &b)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "ioctl() failed", strerror(errno)); + } + + if (b > 0) { + char buf[1024]; + log_error_write(srv, __FILE__, __LINE__, "sdd", + "CLOSE-read()", con->fd, b); + + /* */ + read(con->fd, buf, sizeof(buf)); + } else { + /* nothing to read */ + + con->close_timeout_ts = 0; + } + } + + return HANDLER_FINISHED; +} + + +connection *connection_accept(server *srv, server_socket *srv_socket) { + /* accept everything */ + + /* search an empty place */ + int cnt; + sock_addr cnt_addr; + socklen_t cnt_len; + /* accept it and register the fd */ + + cnt_len = sizeof(cnt_addr); + + if (-1 == (cnt = accept(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { + if ((errno != EAGAIN) && + (errno != EINTR)) { + log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno); + } + return NULL; + } else { + connection *con; + + srv->cur_fds++; + + /* ok, we have the connection, register it */ +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "appected()", cnt); +#endif + srv->con_opened++; + + con = connections_get_new_connection(srv); + + con->fd = cnt; + con->fde_ndx = -1; +#if 0 + gettimeofday(&(con->start_tv), NULL); +#endif + fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); + + connection_set_state(srv, con, CON_STATE_REQUEST_START); + + con->connection_start = srv->cur_ts; + con->dst_addr = cnt_addr; + buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + con->srv_socket = srv_socket; + + if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + return NULL; + } +#ifdef USE_OPENSSL + /* connect FD to SSL */ + if (srv_socket->is_ssl) { + if (NULL == (con->ssl = SSL_new(srv_socket->ssl_ctx))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + + return NULL; + } + + SSL_set_accept_state(con->ssl); + con->conf.is_ssl=1; + + if (1 != (SSL_set_fd(con->ssl, cnt))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } +#endif + return con; + } +} + + +int connection_state_machine(server *srv, connection *con) { + int done = 0, r; +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at start", + con->fd, + connection_get_state(con->state)); + } + + while (done == 0) { + size_t ostate = con->state; + int b; + + switch (con->state) { + case CON_STATE_REQUEST_START: /* transient */ + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; + + con->request_count++; + con->loops_per_request = 0; + + connection_set_state(srv, con, CON_STATE_READ); + + break; + case CON_STATE_REQUEST_END: /* transient */ + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (http_request_parse(srv, con)) { + /* we have to read some data from the POST request */ + + connection_set_state(srv, con, CON_STATE_READ_POST); + + break; + } + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case CON_STATE_HANDLE_REQUEST: + /* + * the request is parsed + * + * decided what to do with the request + * - + * + * + */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + switch (r = http_response_prepare(srv, con)) { + case HANDLER_FINISHED: + if (con->http_status == 404 || + con->http_status == 403) { + /* 404 error-handler */ + + if (con->in_error_handler == 0 && + (!buffer_is_empty(con->conf.error_handler) || + !buffer_is_empty(con->error_handler))) { + /* call error-handler */ + + con->error_handler_saved_status = con->http_status; + con->http_status = 0; + + if (buffer_is_empty(con->error_handler)) { + buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); + } else { + buffer_copy_string_buffer(con->request.uri, con->error_handler); + } + buffer_reset(con->physical.path); + + con->in_error_handler = 1; + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + done = -1; + break; + } else if (con->in_error_handler) { + /* error-handler is a 404 */ + + /* continue as normal, status is the same */ + log_error_write(srv, __FILE__, __LINE__, "sb", + "Warning: Either the error-handler returned status 404 or the error-handler itself was not found:", con->request.uri); + log_error_write(srv, __FILE__, __LINE__, "sd", + "returning the original status", con->error_handler_saved_status); + log_error_write(srv, __FILE__, __LINE__, "s", + "If this is a rails app: check your production.log"); + con->http_status = con->error_handler_saved_status; + } + } else if (con->in_error_handler) { + /* error-handler is back and has generated content */ + /* if Status: was set, take it otherwise use 200 */ + } + + if (con->http_status == 0) con->http_status = 200; + + /* we have something to send, go on */ + connection_set_state(srv, con, CON_STATE_RESPONSE_START); + break; + case HANDLER_WAIT_FOR_FD: + srv->want_fds++; + + fdwaitqueue_append(srv, con); + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case HANDLER_COMEBACK: + done = -1; + case HANDLER_WAIT_FOR_EVENT: + /* come back here */ + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case HANDLER_ERROR: + /* something went wrong */ + connection_set_state(srv, con, CON_STATE_ERROR); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); + break; + } + + break; + case CON_STATE_RESPONSE_START: + /* + * the decision is done + * - create the HTTP-Response-Header + * + */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (-1 == connection_handle_write_prepare(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + break; + } + + connection_set_state(srv, con, CON_STATE_WRITE); + break; + case CON_STATE_RESPONSE_END: /* transient */ + /* log the request */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + plugins_call_handle_request_done(srv, con); + + srv->con_written++; + + if (con->keep_alive) { + connection_set_state(srv, con, CON_STATE_REQUEST_START); + +#if 0 + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; +#endif + } else { + switch(r = plugins_call_handle_connection_close(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); + break; + } + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + switch (SSL_shutdown(con->ssl)) { + case 1: + /* done */ + break; + case 0: + /* wait for fd-event + * + * FIXME: wait for fdevent and call SSL_shutdown again + * + */ + + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + } + } +#endif + connection_close(srv, con); + + srv->con_closed++; + } + + connection_reset(srv, con); + + break; + case CON_STATE_CONNECT: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + chunkqueue_reset(con->read_queue); + + con->request_count = 0; + + break; + case CON_STATE_CLOSE: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (con->keep_alive) { + if (ioctl(con->fd, FIONREAD, &b)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "ioctl() failed", strerror(errno)); + } + if (b > 0) { + char buf[1024]; + log_error_write(srv, __FILE__, __LINE__, "sdd", + "CLOSE-read()", con->fd, b); + + /* */ + read(con->fd, buf, sizeof(buf)); + } else { + /* nothing to read */ + + con->close_timeout_ts = 0; + } + } else { + con->close_timeout_ts = 0; + } + + if (srv->cur_ts - con->close_timeout_ts > 1) { + connection_close(srv, con); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed for fd", con->fd); + } + } + + break; + case CON_STATE_READ_POST: + case CON_STATE_READ: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + connection_handle_read_state(srv, con); + break; + case CON_STATE_WRITE: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + /* only try to write if we have something in the queue */ + if (!chunkqueue_is_empty(con->write_queue)) { +#if 0 + log_error_write(srv, __FILE__, __LINE__, "dsd", + con->fd, + "packets to write:", + con->write_queue->used); +#endif + } + if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) { + if (-1 == connection_handle_write(srv, con)) { + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + connection_set_state(srv, con, CON_STATE_ERROR); + } else if (con->state == CON_STATE_WRITE) { + con->write_request_ts = srv->cur_ts; + } + } + + break; + case CON_STATE_ERROR: /* transient */ + + /* even if the connection was drop we still have to write it to the access log */ + if (con->http_status) { + plugins_call_handle_request_done(srv, con); + } +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + int ret; + switch ((ret = SSL_shutdown(con->ssl))) { + case 1: + /* ok */ + break; + case 0: + SSL_shutdown(con->ssl); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + SSL_get_error(con->ssl, ret), + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } +#endif + + switch(con->mode) { + case DIRECT: +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "emergency exit: direct", + con->fd); +#endif + break; + default: + switch(r = plugins_call_handle_connection_close(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, ""); + break; + } + break; + } + + connection_reset(srv, con); + + /* close the connection */ + if ((con->keep_alive == 1) && + (0 == shutdown(con->fd, SHUT_WR))) { + con->close_timeout_ts = srv->cur_ts; + connection_set_state(srv, con, CON_STATE_CLOSE); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "shutdown for fd", con->fd); + } + } else { + connection_close(srv, con); + } + + con->keep_alive = 0; + + srv->con_closed++; + + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", + "unknown state:", con->fd, con->state); + + break; + } + + if (done == -1) { + done = 0; + } else if (ostate == con->state) { + done = 1; + } + } + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at exit:", + con->fd, + connection_get_state(con->state)); + } + + switch(con->state) { + case CON_STATE_READ_POST: + case CON_STATE_READ: + case CON_STATE_CLOSE: + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); + break; + case CON_STATE_WRITE: + /* request write-fdevent only if we really need it + * - if we have data to write + * - if the socket is not writable yet + */ + if (!chunkqueue_is_empty(con->write_queue) && + (con->is_writable == 0) && + (con->traffic_limit_reached == 0)) { + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); + } else { + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + } + break; + default: + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + break; + } + + return 0; +} diff --git a/src/main/resource/testFiles/lighttpd1.4/revFiles/f27b8d_4a86d3_src#connections.c b/src/main/resource/testFiles/lighttpd1.4/revFiles/f27b8d_4a86d3_src#connections.c new file mode 100644 index 0000000..ea3a66c --- /dev/null +++ b/src/main/resource/testFiles/lighttpd1.4/revFiles/f27b8d_4a86d3_src#connections.c @@ -0,0 +1,1708 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "server.h" +#include "log.h" +#include "connections.h" +#include "fdevent.h" + +#include "request.h" +#include "response.h" +#include "network.h" +#include "http_chunk.h" +#include "stat_cache.h" +#include "joblist.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + +#ifdef USE_OPENSSL +# include +# include +#endif + +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#include "sys-socket.h" + +typedef struct { + PLUGIN_DATA; +} plugin_data; + +static connection *connections_get_new_connection(server *srv) { + connections *conns = srv->conns; + size_t i; + + if (conns->size == 0) { + conns->size = 128; + conns->ptr = NULL; + conns->ptr = malloc(sizeof(*conns->ptr) * conns->size); + for (i = 0; i < conns->size; i++) { + conns->ptr[i] = connection_init(srv); + } + } else if (conns->size == conns->used) { + conns->size += 128; + conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size); + + for (i = conns->used; i < conns->size; i++) { + conns->ptr[i] = connection_init(srv); + } + } + + connection_reset(srv, conns->ptr[conns->used]); +#if 0 + fprintf(stderr, "%s.%d: add: ", __FILE__, __LINE__); + for (i = 0; i < conns->used + 1; i++) { + fprintf(stderr, "%d ", conns->ptr[i]->fd); + } + fprintf(stderr, "\n"); +#endif + + conns->ptr[conns->used]->ndx = conns->used; + return conns->ptr[conns->used++]; +} + +static int connection_del(server *srv, connection *con) { + size_t i; + connections *conns = srv->conns; + connection *temp; + + if (con == NULL) return -1; + + if (-1 == con->ndx) return -1; + + i = con->ndx; + + /* not last element */ + + if (i != conns->used - 1) { + temp = conns->ptr[i]; + conns->ptr[i] = conns->ptr[conns->used - 1]; + conns->ptr[conns->used - 1] = temp; + + conns->ptr[i]->ndx = i; + conns->ptr[conns->used - 1]->ndx = -1; + } + + conns->used--; + + con->ndx = -1; +#if 0 + fprintf(stderr, "%s.%d: del: (%d)", __FILE__, __LINE__, conns->used); + for (i = 0; i < conns->used; i++) { + fprintf(stderr, "%d ", conns->ptr[i]->fd); + } + fprintf(stderr, "\n"); +#endif + return 0; +} + +int connection_close(server *srv, connection *con) { +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + if (con->ssl) SSL_free(con->ssl); + con->ssl = NULL; + } +#endif + + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + fdevent_unregister(srv->ev, con->fd); +#ifdef __WIN32 + if (closesocket(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#else + if (close(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#endif + + srv->cur_fds--; +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "closed()", con->fd); +#endif + + connection_del(srv, con); + connection_set_state(srv, con, CON_STATE_CONNECT); + + return 0; +} + +#if 0 +static void dump_packet(const unsigned char *data, size_t len) { + size_t i, j; + + if (len == 0) return; + + for (i = 0; i < len; i++) { + if (i % 16 == 0) fprintf(stderr, " "); + + fprintf(stderr, "%02x ", data[i]); + + if ((i + 1) % 16 == 0) { + fprintf(stderr, " "); + for (j = 0; j <= i % 16; j++) { + unsigned char c; + + if (i-15+j >= len) break; + + c = data[i-15+j]; + + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + + fprintf(stderr, "\n"); + } + } + + if (len % 16 != 0) { + for (j = i % 16; j < 16; j++) { + fprintf(stderr, " "); + } + + fprintf(stderr, " "); + for (j = i & ~0xf; j < len; j++) { + unsigned char c; + + c = data[j]; + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + fprintf(stderr, "\n"); + } +} +#endif + +static int connection_handle_read(server *srv, connection *con) { + int len; + buffer *b; + int toread; +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + + b = chunkqueue_get_append_buffer(con->read_queue); + buffer_prepare_copy(b, 4096); + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + len = SSL_read(con->ssl, b->ptr, b->size - 1); + } else { + if (ioctl(con->fd, FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "unexpected end-of-file:", + con->fd); + return -1; + } + buffer_prepare_copy(b, toread); + + len = read(con->fd, b->ptr, b->size - 1); + } +#elif defined(__WIN32) + len = recv(con->fd, b->ptr, b->size - 1, 0); +#else + if (ioctl(con->fd, FIONREAD, &toread)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "unexpected end-of-file:", + con->fd); + return -1; + } + buffer_prepare_copy(b, toread); + + len = read(con->fd, b->ptr, b->size - 1); +#endif + + if (len < 0) { + con->is_readable = 0; + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + int r, ssl_err; + + switch ((r = SSL_get_error(con->ssl, len))) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_SYSCALL: + /** + * man SSL_get_error() + * + * SSL_ERROR_SYSCALL + * Some I/O error occurred. The OpenSSL error queue may contain more + * information on the error. If the error queue is empty (i.e. + * ERR_get_error() returns 0), ret can be used to find out more about + * the error: If ret == 0, an EOF was observed that violates the + * protocol. If ret == -1, the underlying BIO reported an I/O error + * (for socket I/O on Unix systems, consult errno for details). + * + */ + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + + switch(errno) { + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + len, r, errno, + strerror(errno)); + break; + } + + break; + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) { + /* FIXME: later */ + } + + /* fall thourgh */ + default: + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + break; + } + } else { + if (errno == EAGAIN) return 0; + if (errno == EINTR) { + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + } + + if (errno != ECONNRESET) { + /* expected for keep-alive */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + } + } +#else + if (errno == EAGAIN) return 0; + if (errno == EINTR) { + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + } + + if (errno != ECONNRESET) { + /* expected for keep-alive */ + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + } +#endif + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + /* pipelining */ + + return -2; + } else if ((size_t)len < b->size - 1) { + /* we got less then expected, wait for the next fd-event */ + + con->is_readable = 0; + } + + b->used = len; + b->ptr[b->used++] = '\0'; + + con->bytes_read += len; +#if 0 + dump_packet(b->ptr, len); +#endif + + return 0; +} + +static int connection_handle_write_prepare(server *srv, connection *con) { + if (con->mode == DIRECT) { + /* static files */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + case HTTP_METHOD_PUT: + case HTTP_METHOD_MKCOL: + case HTTP_METHOD_DELETE: + case HTTP_METHOD_COPY: + case HTTP_METHOD_MOVE: + case HTTP_METHOD_PROPFIND: + case HTTP_METHOD_PROPPATCH: + break; + case HTTP_METHOD_OPTIONS: + /* + * 400 is coming from the request-parser BEFORE uri.path is set + * 403 is from the response handler when noone else catched it + * + * */ + if (con->uri.path->used && + con->uri.path->ptr[0] != '*') { + response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + + con->http_status = 200; + con->file_finished = 1; + + chunkqueue_reset(con->write_queue); + } + break; + default: + switch(con->http_status) { + case 400: /* bad request */ + case 414: /* overload request header */ + case 505: /* unknown protocol */ + case 207: /* this was webdav */ + break; + default: + con->http_status = 501; + break; + } + break; + } + } + + if (con->http_status == 0) { + con->http_status = 403; + } + + switch(con->http_status) { + case 400: /* class: header + custom body */ + case 401: + case 403: + case 404: + case 408: + case 411: + case 416: + case 500: + case 501: + case 503: + case 505: + if (con->mode != DIRECT) break; + + con->file_finished = 0; + + buffer_reset(con->physical.path); + + /* try to send static errorfile */ + if (!buffer_is_empty(con->conf.errorfile_prefix)) { + stat_cache_entry *sce = NULL; + + buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); + buffer_append_string(con->physical.path, get_http_status_body_name(con->http_status)); + + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + con->file_finished = 1; + + http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + } + } + + if (!con->file_finished) { + buffer *b; + + buffer_reset(con->physical.path); + + con->file_finished = 1; + b = chunkqueue_get_append_buffer(con->write_queue); + + /* build default error-page */ + buffer_copy_string(b, + "\n" + "\n" + "\n" + " \n" + " "); + buffer_append_long(b, con->http_status); + buffer_append_string(b, " - "); + buffer_append_string(b, get_http_status_name(con->http_status)); + + buffer_append_string(b, + "\n" + " \n" + " \n" + "

"); + buffer_append_long(b, con->http_status); + buffer_append_string(b, " - "); + buffer_append_string(b, get_http_status_name(con->http_status)); + + buffer_append_string(b,"

\n" + " \n" + "\n" + ); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } + /* fall through */ + case 207: + case 200: /* class: header + body */ + case 302: + break; + + case 206: /* write_queue is already prepared */ + con->file_finished = 1; + + break; + case 205: /* class: header only */ + case 301: + case 304: + default: + /* disable chunked encoding again as we have no body */ + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + chunkqueue_reset(con->write_queue); + + con->file_finished = 1; + break; + } + + + if (con->file_finished) { + /* we have all the content and chunked encoding is not used, set a content-length */ + + if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && + (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { + buffer_copy_off_t(srv->tmp_buf, chunkqueue_length(con->write_queue)); + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); + } + } else { + /* disable keep-alive if size-info for the body is missing */ + if ((con->parsed_response & HTTP_CONTENT_LENGTH) && + ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { + con->keep_alive = 0; + } + + if (0 == (con->parsed_response & HTTP_CONNECTION)) { + /* (f)cgi did'nt send Connection: header + * + * shall we ? + */ + if (((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) && + (con->parsed_response & HTTP_CONTENT_LENGTH) == 0) { + /* without content_length, no keep-alive */ + + con->keep_alive = 0; + } + } else { + /* a subrequest disable keep-alive although the client wanted it */ + if (con->keep_alive && !con->response.keep_alive) { + con->keep_alive = 0; + + /* FIXME: we have to drop the Connection: Header from the subrequest */ + } + } + } + + if (con->request.http_method == HTTP_METHOD_HEAD) { + chunkqueue_reset(con->write_queue); + } + + http_response_write_header(srv, con); + + return 0; +} + +static int connection_handle_write(server *srv, connection *con) { + switch(network_write_chunkqueue(srv, con, con->write_queue)) { + case 0: + if (con->file_finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + joblist_append(srv, con); + } + break; + case -1: /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: write failed on fd", con->fd); + connection_set_state(srv, con, CON_STATE_ERROR); + joblist_append(srv, con); + break; + case -2: /* remote close */ + connection_set_state(srv, con, CON_STATE_ERROR); + joblist_append(srv, con); + break; + case 1: + con->is_writable = 0; + + /* not finished yet -> WRITE */ + break; + } + + return 0; +} + + + +connection *connection_init(server *srv) { + connection *con; + + UNUSED(srv); + + con = calloc(1, sizeof(*con)); + + con->fd = 0; + con->ndx = -1; + con->fde_ndx = -1; + con->bytes_written = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + +#define CLEAN(x) \ + con->x = buffer_init(); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.rel_path); + CLEAN(physical.etag); + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); + CLEAN(dst_addr_buf); + +#undef CLEAN + con->write_queue = chunkqueue_init(); + con->read_queue = chunkqueue_init(); + con->request_content_queue = chunkqueue_init(); + chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs); + + con->request.headers = array_init(); + con->response.headers = array_init(); + con->environment = array_init(); + + /* init plugin specific connection structures */ + + con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); + + con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); + config_setup_connection(srv, con); + + return con; +} + +void connections_free(server *srv) { + connections *conns = srv->conns; + size_t i; + + for (i = 0; i < conns->size; i++) { + connection *con = conns->ptr[i]; + + connection_reset(srv, con); + + chunkqueue_free(con->write_queue); + chunkqueue_free(con->read_queue); + chunkqueue_free(con->request_content_queue); + array_free(con->request.headers); + array_free(con->response.headers); + array_free(con->environment); + +#define CLEAN(x) \ + buffer_free(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.etag); + CLEAN(physical.rel_path); + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); + CLEAN(dst_addr_buf); +#undef CLEAN + free(con->plugin_ctx); + free(con->cond_cache); + + free(con); + } + + free(conns->ptr); +} + + +int connection_reset(server *srv, connection *con) { + size_t i; + + plugins_call_connection_reset(srv, con); + + con->is_readable = 1; + con->is_writable = 1; + con->http_status = 0; + con->file_finished = 0; + con->file_started = 0; + con->got_response = 0; + + con->parsed_response = 0; + + con->bytes_written = 0; + con->bytes_written_cur_second = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + + con->request.http_method = HTTP_METHOD_UNSET; + con->request.http_version = HTTP_VERSION_UNSET; + + con->request.http_if_modified_since = NULL; + con->request.http_if_none_match = NULL; + + con->response.keep_alive = 0; + con->response.content_length = -1; + con->response.transfer_encoding = 0; + + con->mode = DIRECT; + +#define CLEAN(x) \ + if (con->x) buffer_reset(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.pathinfo); + CLEAN(request.request); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.rel_path); + CLEAN(physical.etag); + + CLEAN(parse_request); + + CLEAN(authed_user); + CLEAN(server_name); + CLEAN(error_handler); +#undef CLEAN + +#define CLEAN(x) \ + if (con->x) con->x->used = 0; + +#undef CLEAN + +#define CLEAN(x) \ + con->request.x = NULL; + + CLEAN(http_host); + CLEAN(http_range); + CLEAN(http_content_type); +#undef CLEAN + con->request.content_length = 0; + + array_reset(con->request.headers); + array_reset(con->response.headers); + array_reset(con->environment); + + chunkqueue_reset(con->write_queue); + chunkqueue_reset(con->request_content_queue); + + /* the plugins should cleanup themself */ + for (i = 0; i < srv->plugins.used; i++) { + plugin *p = ((plugin **)(srv->plugins.ptr))[i]; + plugin_data *pd = p->data; + + if (!pd) continue; + + if (con->plugin_ctx[pd->id] != NULL) { + log_error_write(srv, __FILE__, __LINE__, "sb", "missing cleanup in", p->name); + } + + con->plugin_ctx[pd->id] = NULL; + } + +#if COND_RESULT_UNSET + for (i = srv->config_context->used - 1; i >= 0; i --) { + con->cond_cache[i].result = COND_RESULT_UNSET; + con->cond_cache[i].patterncount = 0; + } +#else + memset(con->cond_cache, 0, sizeof(cond_cache_t) * srv->config_context->used); +#endif + + con->header_len = 0; + con->in_error_handler = 0; + + config_setup_connection(srv, con); + + return 0; +} + +/** + * + * search for \r\n\r\n + * + * this is a special 32bit version which is using a sliding window for + * the comparisions + * + * how it works: + * + * b: 'abcdefg' + * rnrn: 'cdef' + * + * cmpbuf: abcd != cdef + * cmpbuf: bcde != cdef + * cmpbuf: cdef == cdef -> return &c + * + * cmpbuf and rnrn are treated as 32bit uint and bit-ops are used to + * maintain cmpbuf and rnrn + * + */ + +char *buffer_search_rnrn(buffer *b) { + uint32_t cmpbuf, rnrn; + char *cp; + size_t i; + + if (b->used < 4) return NULL; + + rnrn = ('\r' << 24) | ('\n' << 16) | + ('\r' << 8) | ('\n' << 0); + + cmpbuf = (b->ptr[0] << 24) | (b->ptr[1] << 16) | + (b->ptr[2] << 8) | (b->ptr[3] << 0); + + cp = b->ptr + 4; + for (i = 0; i < b->used - 4; i++) { + if (cmpbuf == rnrn) return cp - 4; + + cmpbuf = (cmpbuf << 8 | *(cp++)) & 0xffffffff; + } + + return NULL; +} +/** + * handle all header and content read + * + * we get called by the state-engine and by the fdevent-handler + */ +int connection_handle_read_state(server *srv, connection *con) { + int ostate = con->state; + char *h_term = NULL; + chunk *c; + chunkqueue *cq = con->read_queue; + chunkqueue *dst_cq = con->request_content_queue; + + if (con->is_readable) { + con->read_idle_ts = srv->cur_ts; + + switch(connection_handle_read(srv, con)) { + case -1: + return -1; + case -2: + /* remote side closed the connection + * if we still have content, handle it, if not leave here */ + + if (cq->first == cq->last && + cq->first->mem->used == 0) { + + /* conn-closed, leave here */ + connection_set_state(srv, con, CON_STATE_ERROR); + } + default: + break; + } + } + + /* the last chunk might be empty */ + for (c = cq->first; c;) { + if (cq->first == c && c->mem->used == 0) { + /* the first node is empty */ + /* ... and it is empty, move it to unused */ + + cq->first = c->next; + if (cq->first == NULL) cq->last = NULL; + + c->next = cq->unused; + cq->unused = c; + cq->unused_chunks++; + + c = cq->first; + } else if (c->next && c->next->mem->used == 0) { + chunk *fc; + /* next node is the last one */ + /* ... and it is empty, move it to unused */ + + fc = c->next; + c->next = fc->next; + + fc->next = cq->unused; + cq->unused = fc; + cq->unused_chunks++; + + /* the last node was empty */ + if (c->next == NULL) { + cq->last = c; + } + + c = c->next; + } else { + c = c->next; + } + } + + /* nothing to handle */ + if (cq->first == NULL) return 0; + + switch(ostate) { + case CON_STATE_READ: + /* prepare con->request.request */ + c = cq->first; + + /* check if we need the full package */ + if (con->request.request->used == 0) { + buffer b; + + b.ptr = c->mem->ptr + c->offset; + b.used = c->mem->used - c->offset; + + if (NULL != (h_term = buffer_search_rnrn(&b))) { + /* \r\n\r\n found + * - copy everything incl. the terminator to request.request + */ + + buffer_copy_string_len(con->request.request, + b.ptr, + h_term - b.ptr + 4); + + /* the buffer has been read up to the terminator */ + c->offset += h_term - b.ptr + 4; + } else { + /* not found, copy everything */ + buffer_copy_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + c->offset = c->mem->used - 1; + } + } else { + /* have to take care of overlapping header terminators */ + + size_t l = con->request.request->used - 2; + char *s = con->request.request->ptr; + buffer b; + + b.ptr = c->mem->ptr + c->offset; + b.used = c->mem->used - c->offset; + + if (con->request.request->used - 1 > 3 && + c->mem->used > 1 && + s[l-2] == '\r' && + s[l-1] == '\n' && + s[l-0] == '\r' && + c->mem->ptr[0] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 1); + c->offset += 1; + + h_term = con->request.request->ptr; + } else if (con->request.request->used - 1 > 2 && + c->mem->used > 2 && + s[l-1] == '\r' && + s[l-0] == '\n' && + c->mem->ptr[0] == '\r' && + c->mem->ptr[1] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 2); + c->offset += 2; + + h_term = con->request.request->ptr; + } else if (con->request.request->used - 1 > 1 && + c->mem->used > 3 && + s[l-0] == '\r' && + c->mem->ptr[0] == '\n' && + c->mem->ptr[1] == '\r' && + c->mem->ptr[2] == '\n') { + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, 3); + c->offset += 3; + + h_term = con->request.request->ptr; + } else if (NULL != (h_term = buffer_search_string_len(&b, "\r\n\r\n", 4))) { + /* \r\n\r\n found + * - copy everything incl. the terminator to request.request + */ + + buffer_append_string_len(con->request.request, + c->mem->ptr + c->offset, + c->offset + h_term - b.ptr + 4); + + /* the buffer has been read up to the terminator */ + c->offset += h_term - b.ptr + 4; + } else { + /* not found, copy everything */ + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); + c->offset = c->mem->used - 1; + } + } + + /* con->request.request is setup up */ + if (h_term) { + connection_set_state(srv, con, CON_STATE_REQUEST_END); + } else if (con->request.request->used > 64 * 1024) { + log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 414"); + + con->http_status = 414; /* Request-URI too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + } + break; + case CON_STATE_READ_POST: + for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) { + off_t weWant, weHave, toRead; + + weWant = con->request.content_length - dst_cq->bytes_in; + + assert(c->mem->used); + + weHave = c->mem->used - c->offset - 1; + + toRead = weHave > weWant ? weWant : weHave; + + /* the new way, copy everything into a chunkqueue whcih might use tempfiles */ + if (con->request.content_length > 64 * 1024) { + chunk *dst_c = NULL; + /* copy everything to max 1Mb sized tempfiles */ + + /* + * if the last chunk is + * - smaller than 1Mb (size < 1Mb) + * - not read yet (offset == 0) + * -> append to it + * otherwise + * -> create a new chunk + * + * */ + + if (dst_cq->last && + dst_cq->last->type == FILE_CHUNK && + dst_cq->last->file.is_temp && + dst_cq->last->offset == 0) { + /* ok, take the last chunk for our job */ + + if (dst_cq->last->file.length < 1 * 1024 * 1024) { + dst_c = dst_cq->last; + + if (dst_c->file.fd == -1) { + /* this should not happen as we cache the fd, but you never know */ + dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND); + } + } else { + /* the chunk is too large now, close it */ + dst_c = dst_cq->last; + + if (dst_c->file.fd != -1) { + close(dst_c->file.fd); + dst_c->file.fd = -1; + } + dst_c = chunkqueue_get_append_tempfile(dst_cq); + } + } else { + dst_c = chunkqueue_get_append_tempfile(dst_cq); + } + + /* we have a chunk, let's write to it */ + + if (dst_c->file.fd == -1) { + /* we don't have file to write to, + * EACCES might be one reason. + * + * Instead of sending 500 we send 413 and say the request is too large + * */ + + log_error_write(srv, __FILE__, __LINE__, "sbs", + "denying upload as opening to temp-file for upload failed:", + dst_c->file.name, strerror(errno)); + + con->http_status = 413; /* Request-Entity too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + } + + if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { + /* write failed for some reason ... disk full ? */ + log_error_write(srv, __FILE__, __LINE__, "sbs", + "denying upload as writing to file failed:", + dst_c->file.name, strerror(errno)); + + con->http_status = 413; /* Request-Entity too large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + close(dst_c->file.fd); + dst_c->file.fd = -1; + + break; + } + + dst_c->file.length += toRead; + + if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) { + /* we read everything, close the chunk */ + close(dst_c->file.fd); + dst_c->file.fd = -1; + } + } else { + buffer *b; + + b = chunkqueue_get_append_buffer(dst_cq); + buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead); + } + + c->offset += toRead; + dst_cq->bytes_in += toRead; + } + + /* Content is ready */ + if (dst_cq->bytes_in == (off_t)con->request.content_length) { + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + } + + break; + } + + chunkqueue_remove_finished_chunks(cq); + + return 0; +} + +handler_t connection_handle_fdevent(void *s, void *context, int revents) { + server *srv = (server *)s; + connection *con = context; + + joblist_append(srv, con); + + if (revents & FDEVENT_IN) { + con->is_readable = 1; +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", "read-wait - done", con->fd); +#endif + } + if (revents & FDEVENT_OUT) { + con->is_writable = 1; + /* we don't need the event twice */ + } + + + if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { + /* looks like an error */ + + /* FIXME: revents = 0x19 still means that we should read from the queue */ + if (revents & FDEVENT_HUP) { + if (con->state == CON_STATE_CLOSE) { + con->close_timeout_ts = 0; + } else { + /* sigio reports the wrong event here + * + * there was no HUP at all + */ +#ifdef USE_LINUX_SIGIO + if (srv->ev->in_sigio == 1) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> HUP", con->fd); + } else { + connection_set_state(srv, con, CON_STATE_ERROR); + } +#else + connection_set_state(srv, con, CON_STATE_ERROR); +#endif + + } + } else if (revents & FDEVENT_ERR) { +#ifndef USE_LINUX_SIGIO + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> ERR", con->fd); +#endif + connection_set_state(srv, con, CON_STATE_ERROR); + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> ???", revents); + } + } + + if (con->state == CON_STATE_READ || + con->state == CON_STATE_READ_POST) { + connection_handle_read_state(srv, con); + } + + if (con->state == CON_STATE_WRITE && + !chunkqueue_is_empty(con->write_queue) && + con->is_writable) { + + if (-1 == connection_handle_write(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + } else if (con->state == CON_STATE_WRITE) { + con->write_request_ts = srv->cur_ts; + } + } + + if (con->state == CON_STATE_CLOSE) { + /* flush the read buffers */ + int b; + + if (ioctl(con->fd, FIONREAD, &b)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "ioctl() failed", strerror(errno)); + } + + if (b > 0) { + char buf[1024]; + log_error_write(srv, __FILE__, __LINE__, "sdd", + "CLOSE-read()", con->fd, b); + + /* */ + read(con->fd, buf, sizeof(buf)); + } else { + /* nothing to read */ + + con->close_timeout_ts = 0; + } + } + + return HANDLER_FINISHED; +} + + +connection *connection_accept(server *srv, server_socket *srv_socket) { + /* accept everything */ + + /* search an empty place */ + int cnt; + sock_addr cnt_addr; + socklen_t cnt_len; + /* accept it and register the fd */ + + cnt_len = sizeof(cnt_addr); + + if (-1 == (cnt = accept(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { + if ((errno != EAGAIN) && + (errno != EINTR)) { + log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno); + } + return NULL; + } else { + connection *con; + + srv->cur_fds++; + + /* ok, we have the connection, register it */ +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "appected()", cnt); +#endif + srv->con_opened++; + + con = connections_get_new_connection(srv); + + con->fd = cnt; + con->fde_ndx = -1; +#if 0 + gettimeofday(&(con->start_tv), NULL); +#endif + fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); + + connection_set_state(srv, con, CON_STATE_REQUEST_START); + + con->connection_start = srv->cur_ts; + con->dst_addr = cnt_addr; + buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + con->srv_socket = srv_socket; + + if (-1 == (fdevent_fcntl_set(srv->ev, con->fd))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + return NULL; + } +#ifdef USE_OPENSSL + /* connect FD to SSL */ + if (srv_socket->is_ssl) { + if (NULL == (con->ssl = SSL_new(srv_socket->ssl_ctx))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + + return NULL; + } + + SSL_set_accept_state(con->ssl); + con->conf.is_ssl=1; + + if (1 != (SSL_set_fd(con->ssl, cnt))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } +#endif + return con; + } +} + + +int connection_state_machine(server *srv, connection *con) { + int done = 0, r; +#ifdef USE_OPENSSL + server_socket *srv_sock = con->srv_socket; +#endif + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at start", + con->fd, + connection_get_state(con->state)); + } + + while (done == 0) { + size_t ostate = con->state; + int b; + + switch (con->state) { + case CON_STATE_REQUEST_START: /* transient */ + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; + + con->request_count++; + con->loops_per_request = 0; + + connection_set_state(srv, con, CON_STATE_READ); + + break; + case CON_STATE_REQUEST_END: /* transient */ + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (http_request_parse(srv, con)) { + /* we have to read some data from the POST request */ + + connection_set_state(srv, con, CON_STATE_READ_POST); + + break; + } + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case CON_STATE_HANDLE_REQUEST: + /* + * the request is parsed + * + * decided what to do with the request + * - + * + * + */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + switch (r = http_response_prepare(srv, con)) { + case HANDLER_FINISHED: + if (con->http_status == 404 || + con->http_status == 403) { + /* 404 error-handler */ + + if (con->in_error_handler == 0 && + (!buffer_is_empty(con->conf.error_handler) || + !buffer_is_empty(con->error_handler))) { + /* call error-handler */ + + con->error_handler_saved_status = con->http_status; + con->http_status = 0; + + if (buffer_is_empty(con->error_handler)) { + buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); + } else { + buffer_copy_string_buffer(con->request.uri, con->error_handler); + } + buffer_reset(con->physical.path); + + con->in_error_handler = 1; + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + done = -1; + break; + } else if (con->in_error_handler) { + /* error-handler is a 404 */ + + /* continue as normal, status is the same */ + log_error_write(srv, __FILE__, __LINE__, "sb", + "Warning: Either the error-handler returned status 404 or the error-handler itself was not found:", con->request.uri); + log_error_write(srv, __FILE__, __LINE__, "sd", + "returning the original status", con->error_handler_saved_status); + log_error_write(srv, __FILE__, __LINE__, "s", + "If this is a rails app: check your production.log"); + con->http_status = con->error_handler_saved_status; + } + } else if (con->in_error_handler) { + /* error-handler is back and has generated content */ + /* if Status: was set, take it otherwise use 200 */ + } + + if (con->http_status == 0) con->http_status = 200; + + /* we have something to send, go on */ + connection_set_state(srv, con, CON_STATE_RESPONSE_START); + break; + case HANDLER_WAIT_FOR_FD: + srv->want_fds++; + + fdwaitqueue_append(srv, con); + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case HANDLER_COMEBACK: + done = -1; + case HANDLER_WAIT_FOR_EVENT: + /* come back here */ + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case HANDLER_ERROR: + /* something went wrong */ + connection_set_state(srv, con, CON_STATE_ERROR); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); + break; + } + + break; + case CON_STATE_RESPONSE_START: + /* + * the decision is done + * - create the HTTP-Response-Header + * + */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (-1 == connection_handle_write_prepare(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + break; + } + + connection_set_state(srv, con, CON_STATE_WRITE); + break; + case CON_STATE_RESPONSE_END: /* transient */ + /* log the request */ + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + plugins_call_handle_request_done(srv, con); + + srv->con_written++; + + if (con->keep_alive) { + connection_set_state(srv, con, CON_STATE_REQUEST_START); + +#if 0 + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; +#endif + } else { + switch(r = plugins_call_handle_connection_close(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "unhandling return value", r); + break; + } + +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + switch (SSL_shutdown(con->ssl)) { + case 1: + /* done */ + break; + case 0: + /* wait for fd-event + * + * FIXME: wait for fdevent and call SSL_shutdown again + * + */ + + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + } + } +#endif + connection_close(srv, con); + + srv->con_closed++; + } + + connection_reset(srv, con); + + break; + case CON_STATE_CONNECT: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + chunkqueue_reset(con->read_queue); + + con->request_count = 0; + + break; + case CON_STATE_CLOSE: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + if (con->keep_alive) { + if (ioctl(con->fd, FIONREAD, &b)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "ioctl() failed", strerror(errno)); + } + if (b > 0) { + char buf[1024]; + log_error_write(srv, __FILE__, __LINE__, "sdd", + "CLOSE-read()", con->fd, b); + + /* */ + read(con->fd, buf, sizeof(buf)); + } else { + /* nothing to read */ + + con->close_timeout_ts = 0; + } + } else { + con->close_timeout_ts = 0; + } + + if (srv->cur_ts - con->close_timeout_ts > 1) { + connection_close(srv, con); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed for fd", con->fd); + } + } + + break; + case CON_STATE_READ_POST: + case CON_STATE_READ: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + connection_handle_read_state(srv, con); + break; + case CON_STATE_WRITE: + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + /* only try to write if we have something in the queue */ + if (!chunkqueue_is_empty(con->write_queue)) { +#if 0 + log_error_write(srv, __FILE__, __LINE__, "dsd", + con->fd, + "packets to write:", + con->write_queue->used); +#endif + } + if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) { + if (-1 == connection_handle_write(srv, con)) { + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + connection_set_state(srv, con, CON_STATE_ERROR); + } else if (con->state == CON_STATE_WRITE) { + con->write_request_ts = srv->cur_ts; + } + } + + break; + case CON_STATE_ERROR: /* transient */ + + /* even if the connection was drop we still have to write it to the access log */ + if (con->http_status) { + plugins_call_handle_request_done(srv, con); + } +#ifdef USE_OPENSSL + if (srv_sock->is_ssl) { + int ret; + switch ((ret = SSL_shutdown(con->ssl))) { + case 1: + /* ok */ + break; + case 0: + SSL_shutdown(con->ssl); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + SSL_get_error(con->ssl, ret), + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } +#endif + + switch(con->mode) { + case DIRECT: +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "emergency exit: direct", + con->fd); +#endif + break; + default: + switch(r = plugins_call_handle_connection_close(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, ""); + break; + } + break; + } + + connection_reset(srv, con); + + /* close the connection */ + if ((con->keep_alive == 1) && + (0 == shutdown(con->fd, SHUT_WR))) { + con->close_timeout_ts = srv->cur_ts; + connection_set_state(srv, con, CON_STATE_CLOSE); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "shutdown for fd", con->fd); + } + } else { + connection_close(srv, con); + } + + con->keep_alive = 0; + + srv->con_closed++; + + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", + "unknown state:", con->fd, con->state); + + break; + } + + if (done == -1) { + done = 0; + } else if (ostate == con->state) { + done = 1; + } + } + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at exit:", + con->fd, + connection_get_state(con->state)); + } + + switch(con->state) { + case CON_STATE_READ_POST: + case CON_STATE_READ: + case CON_STATE_CLOSE: + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN); + break; + case CON_STATE_WRITE: + /* request write-fdevent only if we really need it + * - if we have data to write + * - if the socket is not writable yet + */ + if (!chunkqueue_is_empty(con->write_queue) && + (con->is_writable == 0) && + (con->traffic_limit_reached == 0)) { + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); + } else { + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + } + break; + default: + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + break; + } + + return 0; +} diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_acd809_1d3d39_ext#standard#scanf.c b/src/main/resource/testFiles/php-src/prevFiles/prev_acd809_1d3d39_ext#standard#scanf.c new file mode 100644 index 0000000..ae3646d --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_acd809_1d3d39_ext#standard#scanf.c @@ -0,0 +1,1259 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Clayton Collie | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + scanf.c -- + + This file contains the base code which implements sscanf and by extension + fscanf. Original code is from TCL8.3.0 and bears the following copyright + + + + This software is copyrighted by the Regents of the University of + California, Sun Microsystems, Inc., Scriptics Corporation, + and other parties. The following terms apply to all files associated + with the software unless explicitly disclaimed in individual files. + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. + + GOVERNMENT USE: If you are acquiring this software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" + in the software and related documentation as defined in the Federal + Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + are acquiring the software on behalf of the Department of Defense, the + software shall be classified as "Commercial Computer Software" and the + Government shall have only "Restricted Rights" as defined in Clause + 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + authors grant the U.S. Government and others acting in its behalf + permission to use and distribute the software in accordance with the + terms specified in this license. + + */ + +#include +#include +#include +#include "php.h" +#include "php_variables.h" +#ifdef HAVE_LOCALE_H +#include +#endif +#include "zend_execute.h" +#include "zend_operators.h" +#include "php_globals.h" +#include "basic_functions.h" +#include "scanf.h" + +/* + * Flag values used internally by [f|s]canf. + */ + +#define SCAN_NOSKIP 0x1 /* Don't skip blanks. */ +#define SCAN_SUPPRESS 0x2 /* Suppress assignment. */ +#define SCAN_UNSIGNED 0x4 /* Read an unsigned value. */ +#define SCAN_WIDTH 0x8 /* A width value was supplied. */ + +#define SCAN_SIGNOK 0x10 /* A +/- character is allowed. */ +#define SCAN_NODIGITS 0x20 /* No digits have been scanned. */ +#define SCAN_NOZERO 0x40 /* No zero digits have been scanned. */ +#define SCAN_XOK 0x80 /* An 'x' is allowed. */ +#define SCAN_PTOK 0x100 /* Decimal point is allowed. */ +#define SCAN_EXPOK 0x200 /* An exponent is allowed. */ + +#define UCHAR(x) (zend_uchar)(x) + + + +/* + * The following structure contains the information associated with + * a character set. + */ + +typedef struct CharSet { + int exclude; /* 1 if this is an exclusion set. */ + int nchars; + char *chars; + int nranges; + struct Range { + char start; + char end; + } *ranges; +} CharSet; + +/* + * Declarations for functions used only in this file. + */ + +static char *BuildCharSet(CharSet *cset, char *format); +static int CharInSet(CharSet *cset, int ch); +static void ReleaseCharSet(CharSet *cset); +static inline void scan_set_error_return(int numVars, pval **return_value); + + +/* {{{ BuildCharSet + *---------------------------------------------------------------------- + * + * BuildCharSet -- + * + * This function examines a character set format specification + * and builds a CharSet containing the individual characters and + * character ranges specified. + * + * Results: + * Returns the next format position. + * + * Side effects: + * Initializes the charset. + * + *---------------------------------------------------------------------- + */ +static char * BuildCharSet(CharSet *cset, char *format) +{ + char *ch, start; + int nranges; + char *end; + + memset(cset, 0, sizeof(CharSet)); + + ch = format; + if (*ch == '^') { + cset->exclude = 1; + ch = ++format; + } + end = format + 1; /* verify this - cc */ + + /* + * Find the close bracket so we can overallocate the set. + */ + + if (*ch == ']') { + ch = end++; + } + nranges = 0; + while (*ch != ']') { + if (*ch == '-') { + nranges++; + } + ch = end++; + } + + cset->chars = (char *) safe_emalloc(sizeof(char), (end - format - 1), 0); + if (nranges > 0) { + cset->ranges = (struct Range *) safe_emalloc(sizeof(struct Range), nranges, 0); + } else { + cset->ranges = NULL; + } + + /* + * Now build the character set. + */ + + cset->nchars = cset->nranges = 0; + ch = format++; + start = *ch; + if (*ch == ']' || *ch == '-') { + cset->chars[cset->nchars++] = *ch; + ch = format++; + } + while (*ch != ']') { + if (*format == '-') { + /* + * This may be the first character of a range, so don't add + * it yet. + */ + + start = *ch; + } else if (*ch == '-') { + /* + * Check to see if this is the last character in the set, in which + * case it is not a range and we should add the previous character + * as well as the dash. + */ + + if (*format == ']') { + cset->chars[cset->nchars++] = start; + cset->chars[cset->nchars++] = *ch; + } else { + ch = format++; + + /* + * Check to see if the range is in reverse order. + */ + + if (start < *ch) { + cset->ranges[cset->nranges].start = start; + cset->ranges[cset->nranges].end = *ch; + } else { + cset->ranges[cset->nranges].start = *ch; + cset->ranges[cset->nranges].end = start; + } + cset->nranges++; + } + } else { + cset->chars[cset->nchars++] = *ch; + } + ch = format++; + } + return format; +} +/* }}} */ + +/* {{{ CharInSet + *---------------------------------------------------------------------- + * + * CharInSet -- + * + * Check to see if a character matches the given set. + * + * Results: + * Returns non-zero if the character matches the given set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static int CharInSet(CharSet *cset, int c) +{ + char ch = (char) c; + int i, match = 0; + + for (i = 0; i < cset->nchars; i++) { + if (cset->chars[i] == ch) { + match = 1; + break; + } + } + if (!match) { + for (i = 0; i < cset->nranges; i++) { + if ((cset->ranges[i].start <= ch) + && (ch <= cset->ranges[i].end)) { + match = 1; + break; + } + } + } + return (cset->exclude ? !match : match); +} +/* }}} */ + +/* {{{ ReleaseCharSet + *---------------------------------------------------------------------- + * + * ReleaseCharSet -- + * + * Free the storage associated with a character set. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static void ReleaseCharSet(CharSet *cset) +{ + efree((char *)cset->chars); + if (cset->ranges) { + efree((char *)cset->ranges); + } +} +/* }}} */ + +/* {{{ ValidateFormat + *---------------------------------------------------------------------- + * + * ValidateFormat -- + * + * Parse the format string and verify that it is properly formed + * and that there are exactly enough variables on the command line. + * + * Results: + * FAILURE or SUCCESS. + * + * Side effects: + * May set php_error based on abnormal conditions. + * + * Parameters : + * format The format string. + * numVars The number of variables passed to the scan command. + * totalSubs The number of variables that will be required. + * + *---------------------------------------------------------------------- +*/ +PHPAPI int ValidateFormat(char *format, int numVars, int *totalSubs) +{ +#define STATIC_LIST_SIZE 16 + int gotXpg, gotSequential, value, i, flags; + char *end, *ch = NULL; + int staticAssign[STATIC_LIST_SIZE]; + int *nassign = staticAssign; + int objIndex, xpgSize, nspace = STATIC_LIST_SIZE; + TSRMLS_FETCH(); + + /* + * Initialize an array that records the number of times a variable + * is assigned to by the format string. We use this to detect if + * a variable is multiply assigned or left unassigned. + */ + + if (numVars > nspace) { + nassign = (int*)safe_emalloc(sizeof(int), numVars, 0); + nspace = numVars; + } + for (i = 0; i < nspace; i++) { + nassign[i] = 0; + } + + xpgSize = objIndex = gotXpg = gotSequential = 0; + + while (*format != '\0') { + ch = format++; + flags = 0; + + if (*ch != '%') { + continue; + } + ch = format++; + if (*ch == '%') { + continue; + } + if (*ch == '*') { + flags |= SCAN_SUPPRESS; + ch = format++; + goto xpgCheckDone; + } + + if ( isdigit( (int)*ch ) ) { + /* + * Check for an XPG3-style %n$ specification. Note: there + * must not be a mixture of XPG3 specs and non-XPG3 specs + * in the same format string. + */ + + value = strtoul(format-1, &end, 10); + if (*end != '$') { + goto notXpg; + } + format = end+1; + ch = format++; + gotXpg = 1; + if (gotSequential) { + goto mixedXPG; + } + objIndex = value - 1; + if ((objIndex < 0) || (numVars && (objIndex >= numVars))) { + goto badIndex; + } else if (numVars == 0) { + /* + * In the case where no vars are specified, the user can + * specify %9999$ legally, so we have to consider special + * rules for growing the assign array. 'value' is + * guaranteed to be > 0. + */ + + /* set a lower artificial limit on this + * in the interest of security and resource friendliness + * 255 arguments should be more than enough. - cc + */ + if (value > SCAN_MAX_ARGS) { + goto badIndex; + } + + xpgSize = (xpgSize > value) ? xpgSize : value; + } + goto xpgCheckDone; + } + + notXpg: + gotSequential = 1; + if (gotXpg) { + mixedXPG: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "cannot mix \"%\" and \"%n$\" conversion specifiers"); + goto error; + } + + xpgCheckDone: + /* + * Parse any width specifier. + */ + + if (isdigit(UCHAR(*ch))) { + value = strtoul(format-1, &format, 10); + flags |= SCAN_WIDTH; + ch = format++; + } + + /* + * Ignore size specifier. + */ + + if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) { + ch = format++; + } + + if (!(flags & SCAN_SUPPRESS) && numVars && (objIndex >= numVars)) { + goto badIndex; + } + + /* + * Handle the various field types. + */ + + switch (*ch) { + case 'n': + case 'd': + case 'D': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + case 'f': + case 'e': + case 'E': + case 'g': + case 's': + break; + case 'c': + /* we differ here with the TCL implementation in allowing for */ + /* a character width specification, to be more consistent with */ + /* ANSI. since Zend auto allocates space for vars, this is no */ + /* problem - cc */ + /* + if (flags & SCAN_WIDTH) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field width may not be specified in %c conversion"); + goto error; + } + */ + break; + case '[': + if (*format == '\0') { + goto badSet; + } + ch = format++; + if (*ch == '^') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + if (*ch == ']') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + while (*ch != ']') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + break; + badSet: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unmatched [ in format string"); + goto error; + default: + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad scan conversion character \"%c\"", *ch); + goto error; + } + } + if (!(flags & SCAN_SUPPRESS)) { + if (objIndex >= nspace) { + /* + * Expand the nassign buffer. If we are using XPG specifiers, + * make sure that we grow to a large enough size. xpgSize is + * guaranteed to be at least one larger than objIndex. + */ + value = nspace; + if (xpgSize) { + nspace = xpgSize; + } else { + nspace += STATIC_LIST_SIZE; + } + if (nassign == staticAssign) { + nassign = (void *)safe_emalloc(nspace, sizeof(int), 0); + for (i = 0; i < STATIC_LIST_SIZE; ++i) { + nassign[i] = staticAssign[i]; + } + } else { + nassign = (void *)erealloc((void *)nassign, nspace * sizeof(int)); + } + for (i = value; i < nspace; i++) { + nassign[i] = 0; + } + } + nassign[objIndex]++; + objIndex++; + } + } /* while (*format != '\0') */ + + /* + * Verify that all of the variable were assigned exactly once. + */ + + if (numVars == 0) { + if (xpgSize) { + numVars = xpgSize; + } else { + numVars = objIndex; + } + } + if (totalSubs) { + *totalSubs = numVars; + } + for (i = 0; i < numVars; i++) { + if (nassign[i] > 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "Variable is assigned by multiple \"%n$\" conversion specifiers"); + goto error; + } else if (!xpgSize && (nassign[i] == 0)) { + /* + * If the space is empty, and xpgSize is 0 (means XPG wasn't + * used, and/or numVars != 0), then too many vars were given + */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Variable is not assigned by any conversion specifiers"); + goto error; + } + } + + if (nassign != staticAssign) { + efree((char *)nassign); + } + return SCAN_SUCCESS; + +badIndex: + if (gotXpg) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "\"%n$\" argument index out of range"); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Different numbers of variable names and field specifiers"); + } + +error: + if (nassign != staticAssign) { + efree((char *)nassign); + } + return SCAN_ERROR_INVALID_FORMAT; +#undef STATIC_LIST_SIZE +} +/* }}} */ + +/* {{{ php_sscanf_internal + * This is the internal function which does processing on behalf of + * both sscanf() and fscanf() + * + * parameters : + * string literal string to be processed + * format format string + * argCount total number of elements in the args array + * args arguments passed in from user function (f|s)scanf + * varStart offset (in args) of 1st variable passed in to (f|s)scanf + * return_value set with the results of the scan + */ + +PHPAPI int php_sscanf_internal( char *string, char *format, + int argCount, zval ***args, + int varStart, pval **return_value TSRMLS_DC) +{ + int numVars, nconversions, totalVars = -1; + int i, value, result; + int objIndex; + char *end, *baseString; + zval **current; + char op = 0; + int base = 0; + int underflow = 0; + size_t width; + long (*fn)() = NULL; + char *ch, sch; + int flags; + char buf[64]; /* Temporary buffer to hold scanned + * number strings before they are + * passed to strtoul. */ + + + /* do some sanity checking */ + if ((varStart > argCount) || (varStart < 0)){ + varStart = SCAN_MAX_ARGS + 1; + } + numVars = argCount - varStart; + if (numVars < 0) { + numVars = 0; + } + +#if 0 + zend_printf("
in sscanf_internal :
string is \"%s\", format = \"%s\"
NumVars = %d. VarStart = %d
-------------------------
", + string, format, numVars, varStart); +#endif + /* + * Check for errors in the format string. + */ + if (ValidateFormat(format, numVars, &totalVars) != SCAN_SUCCESS) { + scan_set_error_return( numVars, return_value ); + return SCAN_ERROR_INVALID_FORMAT; + } + + objIndex = numVars ? varStart : 0; + + /* + * If any variables are passed, make sure they are all passed by reference + */ + if (numVars) { + for (i = varStart;i < argCount;i++){ + if ( ! PZVAL_IS_REF( *args[ i ] ) ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter %d must be passed by reference", i); + scan_set_error_return(numVars, return_value); + return SCAN_ERROR_VAR_PASSED_BYVAL; + } + } + } + + + /* + * Allocate space for the result objects. Only happens when no variables + * are specified + */ + + if (!numVars) { + /* allocate an array for return */ + array_init(*return_value); + + for (i = 0; i < totalVars; i++) { + if (add_next_index_null(*return_value) == FAILURE) { + scan_set_error_return(0, return_value); + return FAILURE; + } + } + } + + baseString = string; + + /* + * Iterate over the format string filling in the result objects until + * we reach the end of input, the end of the format string, or there + * is a mismatch. + */ + + nconversions = 0; + /* note ! - we need to limit the loop for objIndex to keep it in bounds */ + + while (*format != '\0') { + + ch = format++; + + flags = 0; + + /* + * If we see whitespace in the format, skip whitespace in the string. + */ + + if ( isspace( (int)*ch ) ) { + sch = *string; + while ( isspace( (int)sch ) ) { + if (*string == '\0') { + goto done; + } + string++; + sch = *string; + } + continue; + } + + if (*ch != '%') { + literal: + if (*string == '\0') { + underflow = 1; + goto done; + } + sch = *string; + string++; + if (*ch != sch) { + goto done; + } + continue; + } + + ch = format++; + if (*ch == '%') { + goto literal; + } + + /* + * Check for assignment suppression ('*') or an XPG3-style + * assignment ('%n$'). + */ + + if (*ch == '*') { + flags |= SCAN_SUPPRESS; + ch = format++; + } else if ( isdigit(UCHAR(*ch))) { + value = strtoul(format-1, &end, 10); + if (*end == '$') { + format = end+1; + ch = format++; + objIndex = varStart + value; + } + } + + /* + * Parse any width specifier. + */ + + if ( isdigit(UCHAR(*ch))) { + width = strtoul(format-1, &format, 10); + ch = format++; + } else { + width = 0; + } + + /* + * Ignore size specifier. + */ + + if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) { + ch = format++; + } + + /* + * Handle the various field types. + */ + + switch (*ch) { + case 'n': + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + zend_uint refcount; + + current = args[objIndex++]; + refcount = (*current)->refcount; + zval_dtor( *current ); + ZVAL_LONG( *current, (long)(string - baseString) ); + (*current)->refcount = refcount; + (*current)->is_ref = 1; + } else { + add_index_long(*return_value, objIndex++, string - baseString); + } + } + nconversions++; + continue; + + case 'd': + case 'D': + op = 'i'; + base = 10; + fn = (long (*)())strtol; + break; + case 'i': + op = 'i'; + base = 0; + fn = (long (*)())strtol; + break; + case 'o': + op = 'i'; + base = 8; + fn = (long (*)())strtol; + break; + case 'x': + op = 'i'; + base = 16; + fn = (long (*)())strtol; + break; + case 'u': + op = 'i'; + base = 10; + flags |= SCAN_UNSIGNED; + fn = (long (*)())strtoul; + break; + + case 'f': + case 'e': + case 'E': + case 'g': + op = 'f'; + break; + + case 's': + op = 's'; + break; + + case 'c': + op = 's'; + flags |= SCAN_NOSKIP; + /*-cc-*/ + if (0 == width) { + width = 1; + } + /*-cc-*/ + break; + case '[': + op = '['; + flags |= SCAN_NOSKIP; + break; + } /* switch */ + + /* + * At this point, we will need additional characters from the + * string to proceed. + */ + + if (*string == '\0') { + underflow = 1; + goto done; + } + + /* + * Skip any leading whitespace at the beginning of a field unless + * the format suppresses this behavior. + */ + + if (!(flags & SCAN_NOSKIP)) { + while (*string != '\0') { + sch = *string; + if (! isspace((int)sch) ) { + break; + } + string++; + } + if (*string == '\0') { + underflow = 1; + goto done; + } + } + + /* + * Perform the requested scanning operation. + */ + + switch (op) { + case 'c': + case 's': + /* + * Scan a string up to width characters or whitespace. + */ + + if (width == 0) { + width = (size_t) ~0; + } + end = string; + while (*end != '\0') { + sch = *end; + if ( isspace( (int)sch ) ) { + break; + } + end++; + if (--width == 0) { + break; + } + } + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + zend_uint refcount; + + current = args[objIndex++]; + refcount = (*current)->refcount; + zval_dtor( *current ); + ZVAL_STRINGL( *current, string, end-string, 1); + (*current)->refcount = refcount; + (*current)->is_ref = 1; + } else { + add_index_stringl( *return_value, objIndex++, string, end-string, 1); + } + } + string = end; + break; + + case '[': { + CharSet cset; + + if (width == 0) { + width = (size_t) ~0; + } + end = string; + + format = BuildCharSet(&cset, format); + while (*end != '\0') { + sch = *end; + if (!CharInSet(&cset, (int)sch)) { + break; + } + end++; + if (--width == 0) { + break; + } + } + ReleaseCharSet(&cset); + + if (string == end) { + /* + * Nothing matched the range, stop processing + */ + goto done; + } + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + current = args[objIndex++]; + convert_to_string( *current ); + ZVAL_STRINGL( *current, string, end-string, 1); + } else { + add_index_stringl(*return_value, objIndex++, string, end-string, 1); + } + } + string = end; + + break; + } + /* + case 'c': + / Scan a single character./ + + sch = *string; + string++; + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + char __buf[2]; + __buf[0] = sch; + __buf[1] = '\0';; + current = args[objIndex++]; + convert_to_string_ex( current ); + ZVAL_STRINGL( *current, __buf, 1, 1); + } else { + add_index_stringl(*return_value, objIndex++, &sch, 1, 1); + } + } + break; + */ + case 'i': + /* + * Scan an unsigned or signed integer. + */ + + /*-cc-*/ + buf[0] = '\0'; + /*-cc-*/ + if ((width == 0) || (width > sizeof(buf) - 1)) { + width = sizeof(buf) - 1; + } + + flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO; + for (end = buf; width > 0; width--) { + switch (*string) { + /* + * The 0 digit has special meaning at the beginning of + * a number. If we are unsure of the base, it + * indicates that we are in base 8 or base 16 (if it is + * followed by an 'x'). + */ + case '0': + /*-cc-*/ + if (base == 16) { + flags |= SCAN_XOK; + } + /*-cc-*/ + if (base == 0) { + base = 8; + flags |= SCAN_XOK; + } + if (flags & SCAN_NOZERO) { + flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO); + } else { + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + } + goto addToInt; + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + if (base == 0) { + base = 10; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case '8': case '9': + if (base == 0) { + base = 10; + } + if (base <= 8) { + break; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base <= 10) { + break; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case '+': case '-': + if (flags & SCAN_SIGNOK) { + flags &= ~SCAN_SIGNOK; + goto addToInt; + } + break; + + case 'x': case 'X': + if ((flags & SCAN_XOK) && (end == buf+1)) { + base = 16; + flags &= ~SCAN_XOK; + goto addToInt; + } + break; + } + + /* + * We got an illegal character so we are done accumulating. + */ + + break; + + addToInt: + /* + * Add the character to the temporary buffer. + */ + *end++ = *string++; + if (*string == '\0') { + break; + } + } + + /* + * Check to see if we need to back up because we only got a + * sign or a trailing x after a 0. + */ + + if (flags & SCAN_NODIGITS) { + if (*string == '\0') { + underflow = 1; + } + goto done; + } else if (end[-1] == 'x' || end[-1] == 'X') { + end--; + string--; + } + + + /* + * Scan the value from the temporary buffer. If we are + * returning a large unsigned value, we have to convert it back + * to a string since PHP only supports signed values. + */ + + if (!(flags & SCAN_SUPPRESS)) { + *end = '\0'; + value = (int) (*fn)(buf, NULL, base); + if ((flags & SCAN_UNSIGNED) && (value < 0)) { + sprintf(buf, "%u", value); /* INTL: ISO digit */ + if (numVars) { + /* change passed value type to string */ + current = args[objIndex++]; + convert_to_string( *current ); + ZVAL_STRING( *current, buf, 1 ); + } else { + add_index_string(*return_value, objIndex++, buf, 1); + } + } else { + if (numVars) { + current = args[objIndex++]; + convert_to_long( *current ); + Z_LVAL(**current) = value; + } else { + add_index_long(*return_value, objIndex++, value); + } + } + } + + break; + + case 'f': + /* + * Scan a floating point number + */ + buf[0] = '\0'; /* call me pedantic */ + if ((width == 0) || (width > sizeof(buf) - 1)) { + width = sizeof(buf) - 1; + } + flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK; + for (end = buf; width > 0; width--) { + switch (*string) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS); + goto addToFloat; + case '+': case '-': + if (flags & SCAN_SIGNOK) { + flags &= ~SCAN_SIGNOK; + goto addToFloat; + } + break; + case '.': + if (flags & SCAN_PTOK) { + flags &= ~(SCAN_SIGNOK | SCAN_PTOK); + goto addToFloat; + } + break; + case 'e': case 'E': + /* + * An exponent is not allowed until there has + * been at least one digit. + */ + + if ((flags & (SCAN_NODIGITS | SCAN_EXPOK)) == SCAN_EXPOK) { + flags = (flags & ~(SCAN_EXPOK|SCAN_PTOK)) + | SCAN_SIGNOK | SCAN_NODIGITS; + goto addToFloat; + } + break; + } + + /* + * We got an illegal character so we are done accumulating. + */ + + break; + + addToFloat: + /* + * Add the character to the temporary buffer. + */ + + *end++ = *string++; + if (*string == '\0') { + break; + } + } + + /* + * Check to see if we need to back up because we saw a + * trailing 'e' or sign. + */ + + if (flags & SCAN_NODIGITS) { + if (flags & SCAN_EXPOK) { + /* + * There were no digits at all so scanning has + * failed and we are done. + */ + if (*string == '\0') { + underflow = 1; + } + goto done; + } + + /* + * We got a bad exponent ('e' and maybe a sign). + */ + + end--; + string--; + if (*end != 'e' && *end != 'E') { + end--; + string--; + } + } + + /* + * Scan the value from the temporary buffer. + */ + + if (!(flags & SCAN_SUPPRESS)) { + double dvalue; + *end = '\0'; + dvalue = strtod(buf, NULL); + if (numVars) { + current = args[objIndex++]; + convert_to_double( *current ); + Z_DVAL_PP( current ) = dvalue; + } else { + add_index_double( *return_value, objIndex++, dvalue ); + } + } + break; + } /* switch (op) */ + nconversions++; + } /* while (*format != '\0') */ + +done: + result = SCAN_SUCCESS; + + if (underflow && (0==nconversions)) { + scan_set_error_return( numVars, return_value ); + result = SCAN_ERROR_EOF; + } else if (numVars) { + convert_to_long( *return_value ); + Z_LVAL_PP(return_value) = nconversions; + } else if (nconversions < totalVars) { + /* to do : not all elements converted. we need to prune the list - cc + */ + } + + return result; +} +/* }}} */ + +/* the compiler choked when i tried to make this a macro */ +static inline void scan_set_error_return(int numVars, pval **return_value) +{ + if (numVars) { + Z_TYPE_PP(return_value) = IS_LONG; + Z_LVAL_PP(return_value) = SCAN_ERROR_EOF; /* EOF marker */ + } else { + /* pval_destructor( *return_value ); */ + /* convert_to_null calls destructor */ + convert_to_null( *return_value ); + } +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/prevFiles/prev_cf4a29_40becd_main#safe_mode.c b/src/main/resource/testFiles/php-src/prevFiles/prev_cf4a29_40becd_main#safe_mode.c new file mode 100644 index 0000000..a327502 --- /dev/null +++ b/src/main/resource/testFiles/php-src/prevFiles/prev_cf4a29_40becd_main#safe_mode.c @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Rasmus Lerdorf | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include "php.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif +#include +#include "ext/standard/pageinfo.h" +#include "safe_mode.h" +#include "SAPI.h" +#include "php_globals.h" + + +/* + * php_checkuid + * + * This function has four modes: + * + * 0 - return invalid (0) if file does not exist + * 1 - return valid (1) if file does not exist + * 2 - if file does not exist, check directory + * 3 - only check directory (needed for mkdir) + */ + +PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode) +{ + struct stat sb; + int ret; + long uid=0L, gid=0L, duid=0L, dgid=0L; + char path[MAXPATHLEN]; + char *s; + TSRMLS_FETCH(); + + if (!filename) { + return 0; /* path must be provided */ + } + + if (fopen_mode) { + if (fopen_mode[0] == 'r') { + mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS; + } else { + mode = CHECKUID_CHECK_FILE_AND_DIR; + } + } + + /* + * If given filepath is a URL, allow - safe mode stuff + * related to URL's is checked in individual functions + */ + if (!strncasecmp(filename,"http://", 7) || !strncasecmp(filename,"ftp://", 6)) { + return 1; + } + + /* First we see if the file is owned by the same user... + * If that fails, passthrough and check directory... + */ + if (mode != CHECKUID_ALLOW_ONLY_DIR) { + VCWD_REALPATH(filename, path); + ret = VCWD_STAT(path, &sb); + if (ret < 0) { + if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) { + php_error(E_WARNING, "Unable to access %s", filename); + return 0; + } else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) + php_error(E_WARNING, "Unable to access %s", filename);{ + return 1; + } + } else { + uid = sb.st_uid; + gid = sb.st_gid; + if (uid == php_getuid()) { + return 1; + } else if (PG(safe_mode_gid) && gid == php_getgid()) { + return 1; + } + } + + /* Trim off filename */ + if ((s = strrchr(path, DEFAULT_SLASH))) { + if (s == path) + path[1] = '\0'; + else + *s = '\0'; + } + } else { /* CHECKUID_ALLOW_ONLY_DIR */ + s = strrchr(filename, DEFAULT_SLASH); + + if (s == filename) { + /* root dir */ + path[0] = DEFAULT_SLASH; + path[1] = '\0'; + } else if (s) { + *s = '\0'; + VCWD_REALPATH(filename, path); + *s = DEFAULT_SLASH; + } else { + VCWD_GETCWD(path, MAXPATHLEN); + } + } /* end CHECKUID_ALLOW_ONLY_DIR */ + + if (mode != CHECKUID_ALLOW_ONLY_FILE) { + /* check directory */ + ret = VCWD_STAT(path, &sb); + if (ret < 0) { + php_error(E_WARNING, "Unable to access %s", filename); + return 0; + } + duid = sb.st_uid; + dgid = sb.st_gid; + if (duid == php_getuid()) { + return 1; + } else if (PG(safe_mode_gid) && dgid == php_getgid()) { + return 1; + } else { + TSRMLS_FETCH(); + + if (SG(rfc1867_uploaded_files)) { + if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) { + return 1; + } + } + } + } + + if (mode == CHECKUID_ALLOW_ONLY_DIR) { + uid = duid; + gid = dgid; + if (s) { + *s = 0; + } + } + if (PG(safe_mode_gid)) { + php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid); + } else { + php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid); + } + return 0; +} + + +PHPAPI char *php_get_current_user() +{ + struct passwd *pwd; + struct stat *pstat; + TSRMLS_FETCH(); + + if (SG(request_info).current_user) { + return SG(request_info).current_user; + } + + /* FIXME: I need to have this somehow handled if + USE_SAPI is defined, because cgi will also be + interfaced in USE_SAPI */ + + pstat = sapi_get_stat(TSRMLS_C); + + if (!pstat) { + return empty_string; + } + + if ((pwd=getpwuid(pstat->st_uid))==NULL) { + return empty_string; + } + SG(request_info).current_user_length = strlen(pwd->pw_name); + SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length); + + return SG(request_info).current_user; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/acd809_1d3d39_ext#standard#scanf.c b/src/main/resource/testFiles/php-src/revFiles/acd809_1d3d39_ext#standard#scanf.c new file mode 100644 index 0000000..e45d77f --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/acd809_1d3d39_ext#standard#scanf.c @@ -0,0 +1,1260 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Clayton Collie | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + scanf.c -- + + This file contains the base code which implements sscanf and by extension + fscanf. Original code is from TCL8.3.0 and bears the following copyright + + + + This software is copyrighted by the Regents of the University of + California, Sun Microsystems, Inc., Scriptics Corporation, + and other parties. The following terms apply to all files associated + with the software unless explicitly disclaimed in individual files. + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. + + GOVERNMENT USE: If you are acquiring this software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" + in the software and related documentation as defined in the Federal + Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + are acquiring the software on behalf of the Department of Defense, the + software shall be classified as "Commercial Computer Software" and the + Government shall have only "Restricted Rights" as defined in Clause + 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + authors grant the U.S. Government and others acting in its behalf + permission to use and distribute the software in accordance with the + terms specified in this license. + + */ + +#include +#include +#include +#include "php.h" +#include "php_variables.h" +#ifdef HAVE_LOCALE_H +#include +#endif +#include "zend_execute.h" +#include "zend_operators.h" +#include "php_globals.h" +#include "basic_functions.h" +#include "scanf.h" + +/* + * Flag values used internally by [f|s]canf. + */ + +#define SCAN_NOSKIP 0x1 /* Don't skip blanks. */ +#define SCAN_SUPPRESS 0x2 /* Suppress assignment. */ +#define SCAN_UNSIGNED 0x4 /* Read an unsigned value. */ +#define SCAN_WIDTH 0x8 /* A width value was supplied. */ + +#define SCAN_SIGNOK 0x10 /* A +/- character is allowed. */ +#define SCAN_NODIGITS 0x20 /* No digits have been scanned. */ +#define SCAN_NOZERO 0x40 /* No zero digits have been scanned. */ +#define SCAN_XOK 0x80 /* An 'x' is allowed. */ +#define SCAN_PTOK 0x100 /* Decimal point is allowed. */ +#define SCAN_EXPOK 0x200 /* An exponent is allowed. */ + +#define UCHAR(x) (zend_uchar)(x) + + + +/* + * The following structure contains the information associated with + * a character set. + */ + +typedef struct CharSet { + int exclude; /* 1 if this is an exclusion set. */ + int nchars; + char *chars; + int nranges; + struct Range { + char start; + char end; + } *ranges; +} CharSet; + +/* + * Declarations for functions used only in this file. + */ + +static char *BuildCharSet(CharSet *cset, char *format); +static int CharInSet(CharSet *cset, int ch); +static void ReleaseCharSet(CharSet *cset); +static inline void scan_set_error_return(int numVars, pval **return_value); + + +/* {{{ BuildCharSet + *---------------------------------------------------------------------- + * + * BuildCharSet -- + * + * This function examines a character set format specification + * and builds a CharSet containing the individual characters and + * character ranges specified. + * + * Results: + * Returns the next format position. + * + * Side effects: + * Initializes the charset. + * + *---------------------------------------------------------------------- + */ +static char * BuildCharSet(CharSet *cset, char *format) +{ + char *ch, start; + int nranges; + char *end; + + memset(cset, 0, sizeof(CharSet)); + + ch = format; + if (*ch == '^') { + cset->exclude = 1; + ch = ++format; + } + end = format + 1; /* verify this - cc */ + + /* + * Find the close bracket so we can overallocate the set. + */ + + if (*ch == ']') { + ch = end++; + } + nranges = 0; + while (*ch != ']') { + if (*ch == '-') { + nranges++; + } + ch = end++; + } + + cset->chars = (char *) safe_emalloc(sizeof(char), (end - format - 1), 0); + if (nranges > 0) { + cset->ranges = (struct Range *) safe_emalloc(sizeof(struct Range), nranges, 0); + } else { + cset->ranges = NULL; + } + + /* + * Now build the character set. + */ + + cset->nchars = cset->nranges = 0; + ch = format++; + start = *ch; + if (*ch == ']' || *ch == '-') { + cset->chars[cset->nchars++] = *ch; + ch = format++; + } + while (*ch != ']') { + if (*format == '-') { + /* + * This may be the first character of a range, so don't add + * it yet. + */ + + start = *ch; + } else if (*ch == '-') { + /* + * Check to see if this is the last character in the set, in which + * case it is not a range and we should add the previous character + * as well as the dash. + */ + + if (*format == ']') { + cset->chars[cset->nchars++] = start; + cset->chars[cset->nchars++] = *ch; + } else { + ch = format++; + + /* + * Check to see if the range is in reverse order. + */ + + if (start < *ch) { + cset->ranges[cset->nranges].start = start; + cset->ranges[cset->nranges].end = *ch; + } else { + cset->ranges[cset->nranges].start = *ch; + cset->ranges[cset->nranges].end = start; + } + cset->nranges++; + } + } else { + cset->chars[cset->nchars++] = *ch; + } + ch = format++; + } + return format; +} +/* }}} */ + +/* {{{ CharInSet + *---------------------------------------------------------------------- + * + * CharInSet -- + * + * Check to see if a character matches the given set. + * + * Results: + * Returns non-zero if the character matches the given set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static int CharInSet(CharSet *cset, int c) +{ + char ch = (char) c; + int i, match = 0; + + for (i = 0; i < cset->nchars; i++) { + if (cset->chars[i] == ch) { + match = 1; + break; + } + } + if (!match) { + for (i = 0; i < cset->nranges; i++) { + if ((cset->ranges[i].start <= ch) + && (ch <= cset->ranges[i].end)) { + match = 1; + break; + } + } + } + return (cset->exclude ? !match : match); +} +/* }}} */ + +/* {{{ ReleaseCharSet + *---------------------------------------------------------------------- + * + * ReleaseCharSet -- + * + * Free the storage associated with a character set. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static void ReleaseCharSet(CharSet *cset) +{ + efree((char *)cset->chars); + if (cset->ranges) { + efree((char *)cset->ranges); + } +} +/* }}} */ + +/* {{{ ValidateFormat + *---------------------------------------------------------------------- + * + * ValidateFormat -- + * + * Parse the format string and verify that it is properly formed + * and that there are exactly enough variables on the command line. + * + * Results: + * FAILURE or SUCCESS. + * + * Side effects: + * May set php_error based on abnormal conditions. + * + * Parameters : + * format The format string. + * numVars The number of variables passed to the scan command. + * totalSubs The number of variables that will be required. + * + *---------------------------------------------------------------------- +*/ +PHPAPI int ValidateFormat(char *format, int numVars, int *totalSubs) +{ +#define STATIC_LIST_SIZE 16 + int gotXpg, gotSequential, value, i, flags; + char *end, *ch = NULL; + int staticAssign[STATIC_LIST_SIZE]; + int *nassign = staticAssign; + int objIndex, xpgSize, nspace = STATIC_LIST_SIZE; + TSRMLS_FETCH(); + + /* + * Initialize an array that records the number of times a variable + * is assigned to by the format string. We use this to detect if + * a variable is multiply assigned or left unassigned. + */ + + if (numVars > nspace) { + nassign = (int*)safe_emalloc(sizeof(int), numVars, 0); + nspace = numVars; + } + for (i = 0; i < nspace; i++) { + nassign[i] = 0; + } + + xpgSize = objIndex = gotXpg = gotSequential = 0; + + while (*format != '\0') { + ch = format++; + flags = 0; + + if (*ch != '%') { + continue; + } + ch = format++; + if (*ch == '%') { + continue; + } + if (*ch == '*') { + flags |= SCAN_SUPPRESS; + ch = format++; + goto xpgCheckDone; + } + + if ( isdigit( (int)*ch ) ) { + /* + * Check for an XPG3-style %n$ specification. Note: there + * must not be a mixture of XPG3 specs and non-XPG3 specs + * in the same format string. + */ + + value = strtoul(format-1, &end, 10); + if (*end != '$') { + goto notXpg; + } + format = end+1; + ch = format++; + gotXpg = 1; + if (gotSequential) { + goto mixedXPG; + } + objIndex = value - 1; + if ((objIndex < 0) || (numVars && (objIndex >= numVars))) { + goto badIndex; + } else if (numVars == 0) { + /* + * In the case where no vars are specified, the user can + * specify %9999$ legally, so we have to consider special + * rules for growing the assign array. 'value' is + * guaranteed to be > 0. + */ + + /* set a lower artificial limit on this + * in the interest of security and resource friendliness + * 255 arguments should be more than enough. - cc + */ + if (value > SCAN_MAX_ARGS) { + goto badIndex; + } + + xpgSize = (xpgSize > value) ? xpgSize : value; + } + goto xpgCheckDone; + } + + notXpg: + gotSequential = 1; + if (gotXpg) { + mixedXPG: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "cannot mix \"%\" and \"%n$\" conversion specifiers"); + goto error; + } + + xpgCheckDone: + /* + * Parse any width specifier. + */ + + if (isdigit(UCHAR(*ch))) { + value = strtoul(format-1, &format, 10); + flags |= SCAN_WIDTH; + ch = format++; + } + + /* + * Ignore size specifier. + */ + + if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) { + ch = format++; + } + + if (!(flags & SCAN_SUPPRESS) && numVars && (objIndex >= numVars)) { + goto badIndex; + } + + /* + * Handle the various field types. + */ + + switch (*ch) { + case 'n': + case 'd': + case 'D': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + case 'f': + case 'e': + case 'E': + case 'g': + case 's': + break; + case 'c': + /* we differ here with the TCL implementation in allowing for */ + /* a character width specification, to be more consistent with */ + /* ANSI. since Zend auto allocates space for vars, this is no */ + /* problem - cc */ + /* + if (flags & SCAN_WIDTH) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field width may not be specified in %c conversion"); + goto error; + } + */ + break; + case '[': + if (*format == '\0') { + goto badSet; + } + ch = format++; + if (*ch == '^') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + if (*ch == ']') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + while (*ch != ']') { + if (*format == '\0') { + goto badSet; + } + ch = format++; + } + break; + badSet: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unmatched [ in format string"); + goto error; + default: + { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad scan conversion character \"%c\"", *ch); + goto error; + } + } + if (!(flags & SCAN_SUPPRESS)) { + if (objIndex >= nspace) { + /* + * Expand the nassign buffer. If we are using XPG specifiers, + * make sure that we grow to a large enough size. xpgSize is + * guaranteed to be at least one larger than objIndex. + */ + value = nspace; + if (xpgSize) { + nspace = xpgSize; + } else { + nspace += STATIC_LIST_SIZE; + } + if (nassign == staticAssign) { + nassign = (void *)safe_emalloc(nspace, sizeof(int), 0); + for (i = 0; i < STATIC_LIST_SIZE; ++i) { + nassign[i] = staticAssign[i]; + } + } else { + nassign = (void *)erealloc((void *)nassign, nspace * sizeof(int)); + } + for (i = value; i < nspace; i++) { + nassign[i] = 0; + } + } + nassign[objIndex]++; + objIndex++; + } + } /* while (*format != '\0') */ + + /* + * Verify that all of the variable were assigned exactly once. + */ + + if (numVars == 0) { + if (xpgSize) { + numVars = xpgSize; + } else { + numVars = objIndex; + } + } + if (totalSubs) { + *totalSubs = numVars; + } + for (i = 0; i < numVars; i++) { + if (nassign[i] > 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "Variable is assigned by multiple \"%n$\" conversion specifiers"); + goto error; + } else if (!xpgSize && (nassign[i] == 0)) { + /* + * If the space is empty, and xpgSize is 0 (means XPG wasn't + * used, and/or numVars != 0), then too many vars were given + */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Variable is not assigned by any conversion specifiers"); + goto error; + } + } + + if (nassign != staticAssign) { + efree((char *)nassign); + } + return SCAN_SUCCESS; + +badIndex: + if (gotXpg) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "\"%n$\" argument index out of range"); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Different numbers of variable names and field specifiers"); + } + +error: + if (nassign != staticAssign) { + efree((char *)nassign); + } + return SCAN_ERROR_INVALID_FORMAT; +#undef STATIC_LIST_SIZE +} +/* }}} */ + +/* {{{ php_sscanf_internal + * This is the internal function which does processing on behalf of + * both sscanf() and fscanf() + * + * parameters : + * string literal string to be processed + * format format string + * argCount total number of elements in the args array + * args arguments passed in from user function (f|s)scanf + * varStart offset (in args) of 1st variable passed in to (f|s)scanf + * return_value set with the results of the scan + */ + +PHPAPI int php_sscanf_internal( char *string, char *format, + int argCount, zval ***args, + int varStart, pval **return_value TSRMLS_DC) +{ + int numVars, nconversions, totalVars = -1; + int i, value, result; + int objIndex; + char *end, *baseString; + zval **current; + char op = 0; + int base = 0; + int underflow = 0; + size_t width; + long (*fn)() = NULL; + char *ch, sch; + int flags; + char buf[64]; /* Temporary buffer to hold scanned + * number strings before they are + * passed to strtoul. */ + + + /* do some sanity checking */ + if ((varStart > argCount) || (varStart < 0)){ + varStart = SCAN_MAX_ARGS + 1; + } + numVars = argCount - varStart; + if (numVars < 0) { + numVars = 0; + } + +#if 0 + zend_printf("
in sscanf_internal :
string is \"%s\", format = \"%s\"
NumVars = %d. VarStart = %d
-------------------------
", + string, format, numVars, varStart); +#endif + /* + * Check for errors in the format string. + */ + if (ValidateFormat(format, numVars, &totalVars) != SCAN_SUCCESS) { + scan_set_error_return( numVars, return_value ); + return SCAN_ERROR_INVALID_FORMAT; + } + + objIndex = numVars ? varStart : 0; + + /* + * If any variables are passed, make sure they are all passed by reference + */ + if (numVars) { + for (i = varStart;i < argCount;i++){ + if ( ! PZVAL_IS_REF( *args[ i ] ) ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter %d must be passed by reference", i); + scan_set_error_return(numVars, return_value); + return SCAN_ERROR_VAR_PASSED_BYVAL; + } + } + } + + + /* + * Allocate space for the result objects. Only happens when no variables + * are specified + */ + + if (!numVars) { + /* allocate an array for return */ + array_init(*return_value); + + for (i = 0; i < totalVars; i++) { + if (add_next_index_null(*return_value) == FAILURE) { + scan_set_error_return(0, return_value); + return FAILURE; + } + } + } + + baseString = string; + + /* + * Iterate over the format string filling in the result objects until + * we reach the end of input, the end of the format string, or there + * is a mismatch. + */ + + nconversions = 0; + /* note ! - we need to limit the loop for objIndex to keep it in bounds */ + + while (*format != '\0') { + + ch = format++; + + flags = 0; + + /* + * If we see whitespace in the format, skip whitespace in the string. + */ + + if ( isspace( (int)*ch ) ) { + sch = *string; + while ( isspace( (int)sch ) ) { + if (*string == '\0') { + goto done; + } + string++; + sch = *string; + } + continue; + } + + if (*ch != '%') { + literal: + if (*string == '\0') { + underflow = 1; + goto done; + } + sch = *string; + string++; + if (*ch != sch) { + goto done; + } + continue; + } + + ch = format++; + if (*ch == '%') { + goto literal; + } + + /* + * Check for assignment suppression ('*') or an XPG3-style + * assignment ('%n$'). + */ + + if (*ch == '*') { + flags |= SCAN_SUPPRESS; + ch = format++; + } else if ( isdigit(UCHAR(*ch))) { + value = strtoul(format-1, &end, 10); + if (*end == '$') { + format = end+1; + ch = format++; + objIndex = varStart + value; + } + } + + /* + * Parse any width specifier. + */ + + if ( isdigit(UCHAR(*ch))) { + width = strtoul(format-1, &format, 10); + ch = format++; + } else { + width = 0; + } + + /* + * Ignore size specifier. + */ + + if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) { + ch = format++; + } + + /* + * Handle the various field types. + */ + + switch (*ch) { + case 'n': + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + zend_uint refcount; + + current = args[objIndex++]; + refcount = (*current)->refcount; + zval_dtor( *current ); + ZVAL_LONG( *current, (long)(string - baseString) ); + (*current)->refcount = refcount; + (*current)->is_ref = 1; + } else { + add_index_long(*return_value, objIndex++, string - baseString); + } + } + nconversions++; + continue; + + case 'd': + case 'D': + op = 'i'; + base = 10; + fn = (long (*)())strtol; + break; + case 'i': + op = 'i'; + base = 0; + fn = (long (*)())strtol; + break; + case 'o': + op = 'i'; + base = 8; + fn = (long (*)())strtol; + break; + case 'x': + case 'X': + op = 'i'; + base = 16; + fn = (long (*)())strtol; + break; + case 'u': + op = 'i'; + base = 10; + flags |= SCAN_UNSIGNED; + fn = (long (*)())strtoul; + break; + + case 'f': + case 'e': + case 'E': + case 'g': + op = 'f'; + break; + + case 's': + op = 's'; + break; + + case 'c': + op = 's'; + flags |= SCAN_NOSKIP; + /*-cc-*/ + if (0 == width) { + width = 1; + } + /*-cc-*/ + break; + case '[': + op = '['; + flags |= SCAN_NOSKIP; + break; + } /* switch */ + + /* + * At this point, we will need additional characters from the + * string to proceed. + */ + + if (*string == '\0') { + underflow = 1; + goto done; + } + + /* + * Skip any leading whitespace at the beginning of a field unless + * the format suppresses this behavior. + */ + + if (!(flags & SCAN_NOSKIP)) { + while (*string != '\0') { + sch = *string; + if (! isspace((int)sch) ) { + break; + } + string++; + } + if (*string == '\0') { + underflow = 1; + goto done; + } + } + + /* + * Perform the requested scanning operation. + */ + + switch (op) { + case 'c': + case 's': + /* + * Scan a string up to width characters or whitespace. + */ + + if (width == 0) { + width = (size_t) ~0; + } + end = string; + while (*end != '\0') { + sch = *end; + if ( isspace( (int)sch ) ) { + break; + } + end++; + if (--width == 0) { + break; + } + } + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + zend_uint refcount; + + current = args[objIndex++]; + refcount = (*current)->refcount; + zval_dtor( *current ); + ZVAL_STRINGL( *current, string, end-string, 1); + (*current)->refcount = refcount; + (*current)->is_ref = 1; + } else { + add_index_stringl( *return_value, objIndex++, string, end-string, 1); + } + } + string = end; + break; + + case '[': { + CharSet cset; + + if (width == 0) { + width = (size_t) ~0; + } + end = string; + + format = BuildCharSet(&cset, format); + while (*end != '\0') { + sch = *end; + if (!CharInSet(&cset, (int)sch)) { + break; + } + end++; + if (--width == 0) { + break; + } + } + ReleaseCharSet(&cset); + + if (string == end) { + /* + * Nothing matched the range, stop processing + */ + goto done; + } + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + current = args[objIndex++]; + convert_to_string( *current ); + ZVAL_STRINGL( *current, string, end-string, 1); + } else { + add_index_stringl(*return_value, objIndex++, string, end-string, 1); + } + } + string = end; + + break; + } + /* + case 'c': + / Scan a single character./ + + sch = *string; + string++; + if (!(flags & SCAN_SUPPRESS)) { + if (numVars) { + char __buf[2]; + __buf[0] = sch; + __buf[1] = '\0';; + current = args[objIndex++]; + convert_to_string_ex( current ); + ZVAL_STRINGL( *current, __buf, 1, 1); + } else { + add_index_stringl(*return_value, objIndex++, &sch, 1, 1); + } + } + break; + */ + case 'i': + /* + * Scan an unsigned or signed integer. + */ + + /*-cc-*/ + buf[0] = '\0'; + /*-cc-*/ + if ((width == 0) || (width > sizeof(buf) - 1)) { + width = sizeof(buf) - 1; + } + + flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO; + for (end = buf; width > 0; width--) { + switch (*string) { + /* + * The 0 digit has special meaning at the beginning of + * a number. If we are unsure of the base, it + * indicates that we are in base 8 or base 16 (if it is + * followed by an 'x'). + */ + case '0': + /*-cc-*/ + if (base == 16) { + flags |= SCAN_XOK; + } + /*-cc-*/ + if (base == 0) { + base = 8; + flags |= SCAN_XOK; + } + if (flags & SCAN_NOZERO) { + flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO); + } else { + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + } + goto addToInt; + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + if (base == 0) { + base = 10; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case '8': case '9': + if (base == 0) { + base = 10; + } + if (base <= 8) { + break; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + if (base <= 10) { + break; + } + flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); + goto addToInt; + + case '+': case '-': + if (flags & SCAN_SIGNOK) { + flags &= ~SCAN_SIGNOK; + goto addToInt; + } + break; + + case 'x': case 'X': + if ((flags & SCAN_XOK) && (end == buf+1)) { + base = 16; + flags &= ~SCAN_XOK; + goto addToInt; + } + break; + } + + /* + * We got an illegal character so we are done accumulating. + */ + + break; + + addToInt: + /* + * Add the character to the temporary buffer. + */ + *end++ = *string++; + if (*string == '\0') { + break; + } + } + + /* + * Check to see if we need to back up because we only got a + * sign or a trailing x after a 0. + */ + + if (flags & SCAN_NODIGITS) { + if (*string == '\0') { + underflow = 1; + } + goto done; + } else if (end[-1] == 'x' || end[-1] == 'X') { + end--; + string--; + } + + + /* + * Scan the value from the temporary buffer. If we are + * returning a large unsigned value, we have to convert it back + * to a string since PHP only supports signed values. + */ + + if (!(flags & SCAN_SUPPRESS)) { + *end = '\0'; + value = (int) (*fn)(buf, NULL, base); + if ((flags & SCAN_UNSIGNED) && (value < 0)) { + sprintf(buf, "%u", value); /* INTL: ISO digit */ + if (numVars) { + /* change passed value type to string */ + current = args[objIndex++]; + convert_to_string( *current ); + ZVAL_STRING( *current, buf, 1 ); + } else { + add_index_string(*return_value, objIndex++, buf, 1); + } + } else { + if (numVars) { + current = args[objIndex++]; + convert_to_long( *current ); + Z_LVAL(**current) = value; + } else { + add_index_long(*return_value, objIndex++, value); + } + } + } + + break; + + case 'f': + /* + * Scan a floating point number + */ + buf[0] = '\0'; /* call me pedantic */ + if ((width == 0) || (width > sizeof(buf) - 1)) { + width = sizeof(buf) - 1; + } + flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK; + for (end = buf; width > 0; width--) { + switch (*string) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS); + goto addToFloat; + case '+': case '-': + if (flags & SCAN_SIGNOK) { + flags &= ~SCAN_SIGNOK; + goto addToFloat; + } + break; + case '.': + if (flags & SCAN_PTOK) { + flags &= ~(SCAN_SIGNOK | SCAN_PTOK); + goto addToFloat; + } + break; + case 'e': case 'E': + /* + * An exponent is not allowed until there has + * been at least one digit. + */ + + if ((flags & (SCAN_NODIGITS | SCAN_EXPOK)) == SCAN_EXPOK) { + flags = (flags & ~(SCAN_EXPOK|SCAN_PTOK)) + | SCAN_SIGNOK | SCAN_NODIGITS; + goto addToFloat; + } + break; + } + + /* + * We got an illegal character so we are done accumulating. + */ + + break; + + addToFloat: + /* + * Add the character to the temporary buffer. + */ + + *end++ = *string++; + if (*string == '\0') { + break; + } + } + + /* + * Check to see if we need to back up because we saw a + * trailing 'e' or sign. + */ + + if (flags & SCAN_NODIGITS) { + if (flags & SCAN_EXPOK) { + /* + * There were no digits at all so scanning has + * failed and we are done. + */ + if (*string == '\0') { + underflow = 1; + } + goto done; + } + + /* + * We got a bad exponent ('e' and maybe a sign). + */ + + end--; + string--; + if (*end != 'e' && *end != 'E') { + end--; + string--; + } + } + + /* + * Scan the value from the temporary buffer. + */ + + if (!(flags & SCAN_SUPPRESS)) { + double dvalue; + *end = '\0'; + dvalue = strtod(buf, NULL); + if (numVars) { + current = args[objIndex++]; + convert_to_double( *current ); + Z_DVAL_PP( current ) = dvalue; + } else { + add_index_double( *return_value, objIndex++, dvalue ); + } + } + break; + } /* switch (op) */ + nconversions++; + } /* while (*format != '\0') */ + +done: + result = SCAN_SUCCESS; + + if (underflow && (0==nconversions)) { + scan_set_error_return( numVars, return_value ); + result = SCAN_ERROR_EOF; + } else if (numVars) { + convert_to_long( *return_value ); + Z_LVAL_PP(return_value) = nconversions; + } else if (nconversions < totalVars) { + /* to do : not all elements converted. we need to prune the list - cc + */ + } + + return result; +} +/* }}} */ + +/* the compiler choked when i tried to make this a macro */ +static inline void scan_set_error_return(int numVars, pval **return_value) +{ + if (numVars) { + Z_TYPE_PP(return_value) = IS_LONG; + Z_LVAL_PP(return_value) = SCAN_ERROR_EOF; /* EOF marker */ + } else { + /* pval_destructor( *return_value ); */ + /* convert_to_null calls destructor */ + convert_to_null( *return_value ); + } +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/php-src/revFiles/cf4a29_40becd_main#safe_mode.c b/src/main/resource/testFiles/php-src/revFiles/cf4a29_40becd_main#safe_mode.c new file mode 100644 index 0000000..f4f30d0 --- /dev/null +++ b/src/main/resource/testFiles/php-src/revFiles/cf4a29_40becd_main#safe_mode.c @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Rasmus Lerdorf | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include "php.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif +#include +#include "ext/standard/pageinfo.h" +#include "safe_mode.h" +#include "SAPI.h" +#include "php_globals.h" + + +/* + * php_checkuid + * + * This function has four modes: + * + * 0 - return invalid (0) if file does not exist + * 1 - return valid (1) if file does not exist + * 2 - if file does not exist, check directory + * 3 - only check directory (needed for mkdir) + */ + +PHPAPI int php_checkuid(const char *filename, char *fopen_mode, int mode) +{ + struct stat sb; + int ret; + long uid=0L, gid=0L, duid=0L, dgid=0L; + char path[MAXPATHLEN]; + char *s; + TSRMLS_FETCH(); + + if (!filename) { + return 0; /* path must be provided */ + } + + if (fopen_mode) { + if (fopen_mode[0] == 'r') { + mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS; + } else { + mode = CHECKUID_CHECK_FILE_AND_DIR; + } + } + + /* + * If given filepath is a URL, allow - safe mode stuff + * related to URL's is checked in individual functions + */ + if (!strncasecmp(filename,"http://", 7) || !strncasecmp(filename,"ftp://", 6)) { + return 1; + } + + /* First we see if the file is owned by the same user... + * If that fails, passthrough and check directory... + */ + if (mode != CHECKUID_ALLOW_ONLY_DIR) { + VCWD_REALPATH(filename, path); + ret = VCWD_STAT(path, &sb); + if (ret < 0) { + if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) { + php_error(E_WARNING, "Unable to access %s", filename); + return 0; + } else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) { + php_error(E_WARNING, "Unable to access %s", filename); + return 1; + } + } else { + uid = sb.st_uid; + gid = sb.st_gid; + if (uid == php_getuid()) { + return 1; + } else if (PG(safe_mode_gid) && gid == php_getgid()) { + return 1; + } + } + + /* Trim off filename */ + if ((s = strrchr(path, DEFAULT_SLASH))) { + if (s == path) + path[1] = '\0'; + else + *s = '\0'; + } + } else { /* CHECKUID_ALLOW_ONLY_DIR */ + s = strrchr(filename, DEFAULT_SLASH); + + if (s == filename) { + /* root dir */ + path[0] = DEFAULT_SLASH; + path[1] = '\0'; + } else if (s) { + *s = '\0'; + VCWD_REALPATH(filename, path); + *s = DEFAULT_SLASH; + } else { + VCWD_GETCWD(path, MAXPATHLEN); + } + } /* end CHECKUID_ALLOW_ONLY_DIR */ + + if (mode != CHECKUID_ALLOW_ONLY_FILE) { + /* check directory */ + ret = VCWD_STAT(path, &sb); + if (ret < 0) { + php_error(E_WARNING, "Unable to access %s", filename); + return 0; + } + duid = sb.st_uid; + dgid = sb.st_gid; + if (duid == php_getuid()) { + return 1; + } else if (PG(safe_mode_gid) && dgid == php_getgid()) { + return 1; + } else { + TSRMLS_FETCH(); + + if (SG(rfc1867_uploaded_files)) { + if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) { + return 1; + } + } + } + } + + if (mode == CHECKUID_ALLOW_ONLY_DIR) { + uid = duid; + gid = dgid; + if (s) { + *s = 0; + } + } + if (PG(safe_mode_gid)) { + php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid); + } else { + php_error(E_WARNING, "SAFE MODE Restriction in effect. The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid); + } + return 0; +} + + +PHPAPI char *php_get_current_user() +{ + struct passwd *pwd; + struct stat *pstat; + TSRMLS_FETCH(); + + if (SG(request_info).current_user) { + return SG(request_info).current_user; + } + + /* FIXME: I need to have this somehow handled if + USE_SAPI is defined, because cgi will also be + interfaced in USE_SAPI */ + + pstat = sapi_get_stat(TSRMLS_C); + + if (!pstat) { + return empty_string; + } + + if ((pwd=getpwuid(pstat->st_uid))==NULL) { + return empty_string; + } + SG(request_info).current_user_length = strlen(pwd->pw_name); + SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length); + + return SG(request_info).current_user; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_87646b_2cc0fb_epan#proto.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_87646b_2cc0fb_epan#proto.c new file mode 100644 index 0000000..83adf4e --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_87646b_2cc0fb_epan#proto.c @@ -0,0 +1,7437 @@ +/* proto.c + * Routines for protocol tree + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "packet.h" +#include "ptvcursor.h" +#include "strutil.h" +#include "addr_resolv.h" +#include "oids.h" +#include "plugins.h" +#include "proto.h" +#include "epan_dissect.h" +#include "tvbuff.h" +#include "emem.h" +#include "charsets.h" +#include "asm_utils.h" +#include "column-utils.h" +#include "to_str.h" +#include "expert.h" +#include "show_exception.h" + +#include "wspython/wspy_register.h" + +#define SUBTREE_ONCE_ALLOCATION_NUMBER 8 +#define SUBTREE_MAX_LEVELS 256 +/* Throw an exception if we exceed this many tree items. */ +/* XXX - This should probably be a preference */ +#define MAX_TREE_ITEMS (1 * 1000 * 1000) + + +typedef struct __subtree_lvl { + gint cursor_offset; + proto_item *it; + proto_tree *tree; +} subtree_lvl; + +struct ptvcursor { + subtree_lvl *pushed_tree; + guint8 pushed_tree_index; + guint8 pushed_tree_max; + proto_tree *tree; + tvbuff_t *tvb; + gint offset; +}; + +/* Candidates for assembler */ +static int +wrs_count_bitshift(const guint32 bitmask) +{ + int bitshift = 0; + + while ((bitmask & (1 << bitshift)) == 0) + bitshift++; + return bitshift; +} + +#define cVALS(x) (const value_string*)(x) + +/** See inlined comments. + @param tree the tree to append this item to + @param hfindex field index + @param hfinfo header_field + @return the header field matching 'hfinfo' */ +#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \ + /* If this item is not referenced we dont have to do much work \ + at all but we should still return a node so that field items \ + below this node (think proto_item_add_subtree()) will still \ + have somewhere to attach to or else filtering will not work \ + (they would be ignored since tree would be NULL). \ + DONT try to fake a node where PTREE_FINFO(tree) is NULL \ + since dissectors that want to do proto_item_set_len() or \ + other operations that dereference this would crash. \ + We fake FT_PROTOCOL unless some clients have requested us \ + not to do so. \ + */ \ + if (!tree) \ + return NULL; \ + PTREE_DATA(tree)->count++; \ + if (PTREE_DATA(tree)->count > MAX_TREE_ITEMS) { \ + if (getenv("WIRESHARK_ABORT_ON_TOO_MANY_ITEMS") != NULL) \ + g_error("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS); \ + /* Let the exception handler add items to the tree */ \ + PTREE_DATA(tree)->count = 0; \ + THROW_MESSAGE(DissectorError, \ + ep_strdup_printf("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS)); \ + } \ + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); \ + if (!(PTREE_DATA(tree)->visible)) { \ + if (PTREE_FINFO(tree)) { \ + if ((hfinfo->ref_type != HF_REF_TYPE_DIRECT) \ + && (hfinfo->type != FT_PROTOCOL || \ + PTREE_DATA(tree)->fake_protocols)) { \ + /* just return tree back to the caller */\ + return tree; \ + } \ + } \ + } + +/** See inlined comments. + @param pi the created protocol item we're about to return */ +#define TRY_TO_FAKE_THIS_REPR(pi) \ + g_assert(pi); \ + if (!(PTREE_DATA(pi)->visible)) { \ + /* If the tree (GUI) isn't visible it's pointless for us to generate the protocol \ + * items string representation */ \ + return pi; \ + } + +static const char *hf_try_val_to_str(guint32 value, const header_field_info *hfinfo); + +static void fill_label_boolean(field_info *fi, gchar *label_str); +static void fill_label_bitfield(field_info *fi, gchar *label_str); +static void fill_label_number(field_info *fi, gchar *label_str, gboolean is_signed); +static void fill_label_number64(field_info *fi, gchar *label_str, gboolean is_signed); + +static const char *hfinfo_number_vals_format(const header_field_info *hfinfo, char buf[32], guint32 value); +static const char *hfinfo_number_value_format(const header_field_info *hfinfo, char buf[32], guint32 value); +static const char *hfinfo_numeric_value_format(const header_field_info *hfinfo, char buf[32], guint32 value); + +static const char* hfinfo_uint64_format(const header_field_info *hfinfo); +static const char* hfinfo_int64_format(const header_field_info *hfinfo); + +static proto_item * +proto_tree_add_node(proto_tree *tree, field_info *fi); + +static void +get_hfi_and_length(header_field_info *hfinfo, tvbuff_t *tvb, const gint start, gint *length, + gint *item_length); + +static field_info * +new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, const gint item_length); + +static field_info * +alloc_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, gint *length); + +static proto_item * +proto_tree_add_pi(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + gint start, gint *length, field_info **pfi); + +static void +proto_tree_set_representation_value(proto_item *pi, const char *format, va_list ap); +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap); + +static void +proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb); +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length); +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length); +static void +proto_tree_set_time(field_info *fi, nstime_t *value_ptr); +static void +proto_tree_set_string(field_info *fi, const char* value); +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length, gint encoding); +static void +proto_tree_set_ax25(field_info *fi, const guint8* value); +static void +proto_tree_set_ax25_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_ether(field_info *fi, const guint8* value); +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value); +static void +proto_tree_set_ipv4(field_info *fi, guint32 value); +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr); +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length); +static void +proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr); +static void +proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding); +static void +proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length); +static void +proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length); +static void +proto_tree_set_boolean(field_info *fi, guint32 value); +static void +proto_tree_set_float(field_info *fi, float value); +static void +proto_tree_set_double(field_info *fi, double value); +static void +proto_tree_set_uint(field_info *fi, guint32 value); +static void +proto_tree_set_int(field_info *fi, gint32 value); +static void +proto_tree_set_uint64(field_info *fi, guint64 value); +static void +proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, guint length, const guint encoding); +static void +proto_tree_set_eui64(field_info *fi, const guint64 value); +static void +proto_tree_set_eui64_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding); +static gboolean +proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset, + const int len, const gint ett, const gint **fields, + const guint encoding, const int flags, + gboolean first); + +static int proto_register_field_init(header_field_info *hfinfo, const int parent); + +/* special-case header field used within proto.c */ +static header_field_info hfi_text_only = + { "Text item", "text", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }; +int hf_text_only = -1; + +/* Structure for information about a protocol */ +struct _protocol { + const char *name; /* long description */ + const char *short_name; /* short description */ + const char *filter_name; /* name of this protocol in filters */ + int proto_id; /* field ID for this protocol */ + GList *fields; /* fields for this protocol */ + GList *last_field; /* pointer to end of list of fields */ + gboolean is_enabled; /* TRUE if protocol is enabled */ + gboolean can_toggle; /* TRUE if is_enabled can be changed */ + gboolean is_private; /* TRUE is protocol is private */ +}; + +/* List of all protocols */ +static GList *protocols = NULL; + +#define INITIAL_NUM_PROTOCOL_HFINFO 1500 + +/* Contains information about a field when a dissector calls + * proto_tree_add_item. */ +#define FIELD_INFO_NEW(fi) fi = g_slice_new(field_info) +#define FIELD_INFO_FREE(fi) g_slice_free(field_info, fi) + +/* Contains the space for proto_nodes. */ +#define PROTO_NODE_NEW(node) \ + node = g_slice_new(proto_node); \ + node->first_child = NULL; \ + node->last_child = NULL; \ + node->next = NULL; + +#define PROTO_NODE_FREE(node) \ + g_slice_free(proto_node, node) + +/* String space for protocol and field items for the GUI */ +#define ITEM_LABEL_NEW(il) \ + il = g_slice_new(item_label_t); +#define ITEM_LABEL_FREE(il) \ + g_slice_free(item_label_t, il); + +#define PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo) \ + if((guint)hfindex >= gpa_hfinfo.len && getenv("WIRESHARK_ABORT_ON_DISSECTOR_BUG")) \ + g_error("Unregistered hf! index=%d", hfindex); \ + DISSECTOR_ASSERT_HINT((guint)hfindex < gpa_hfinfo.len, "Unregistered hf!"); \ + hfinfo = gpa_hfinfo.hfi[hfindex]; + +/* List which stores protocols and fields that have been registered */ +typedef struct _gpa_hfinfo_t { + guint32 len; + guint32 allocated_len; + header_field_info **hfi; +} gpa_hfinfo_t; +gpa_hfinfo_t gpa_hfinfo; + +/* Balanced tree of abbreviations and IDs */ +static GTree *gpa_name_tree = NULL; +static header_field_info *same_name_hfinfo; + +static void save_same_name_hfinfo(gpointer data) +{ + same_name_hfinfo = (header_field_info*)data; +} + +/* Points to the first element of an array of bits, indexed by + a subtree item type; that array element is TRUE if subtrees of + an item of that type are to be expanded. */ +static guint32 *tree_is_expanded; + +/* Number of elements in that array. */ +int num_tree_types; + +/* Name hashtables for fast detection of duplicate names */ +static GHashTable* proto_names = NULL; +static GHashTable* proto_short_names = NULL; +static GHashTable* proto_filter_names = NULL; + +static gint +proto_compare_name(gconstpointer p1_arg, gconstpointer p2_arg) +{ + const protocol_t *p1 = (const protocol_t *)p1_arg; + const protocol_t *p2 = (const protocol_t *)p2_arg; + + return g_ascii_strcasecmp(p1->short_name, p2->short_name); +} + + +/* initialize data structures and register protocols and fields */ +void +proto_init(void (register_all_protocols_func)(register_cb cb, gpointer client_data), + void (register_all_handoffs_func)(register_cb cb, gpointer client_data), + register_cb cb, + gpointer client_data) +{ + proto_cleanup(); + + proto_names = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); + proto_short_names = g_hash_table_new(wrs_str_hash, g_str_equal); + proto_filter_names = g_hash_table_new(wrs_str_hash, g_str_equal); + + gpa_hfinfo.len = 0; + gpa_hfinfo.allocated_len = 0; + gpa_hfinfo.hfi = NULL; + gpa_name_tree = g_tree_new_full(wrs_strcmp_with_data, NULL, NULL, save_same_name_hfinfo); + + /* Initialize the ftype subsystem */ + ftypes_initialize(); + + /* Register one special-case FT_TEXT_ONLY field for use when + converting wireshark to new-style proto_tree. These fields + are merely strings on the GUI tree; they are not filterable */ + hf_text_only = proto_register_field_init(&hfi_text_only, -1); + + /* Register the pseudo-protocols used for exceptions. */ + register_show_exception(); + + /* Have each built-in dissector register its protocols, fields, + dissector tables, and dissectors to be called through a + handle, and do whatever one-time initialization it needs to + do. */ + register_all_protocols_func(cb, client_data); +#ifdef HAVE_PYTHON + /* Now scan for python protocols */ + if (cb) + (*cb)(RA_PYTHON_REGISTER, NULL, client_data); + register_all_py_protocols_func(); +#endif + +#ifdef HAVE_PLUGINS + /* Now scan for plugins and load all the ones we find, calling + their register routines to do the stuff described above. */ + if (cb) + (*cb)(RA_PLUGIN_REGISTER, NULL, client_data); + init_plugins(); + register_all_plugin_registrations(); +#endif + + /* Now call the "handoff registration" routines of all built-in + dissectors; those routines register the dissector in other + dissectors' handoff tables, and fetch any dissector handles + they need. */ + register_all_handoffs_func(cb, client_data); + +#ifdef HAVE_PYTHON + /* Now do the same with python dissectors */ + if (cb) + (*cb)(RA_PYTHON_HANDOFF, NULL, client_data); + register_all_py_handoffs_func(); +#endif + +#ifdef HAVE_PLUGINS + /* Now do the same with plugins. */ + if (cb) + (*cb)(RA_PLUGIN_HANDOFF, NULL, client_data); + register_all_plugin_handoffs(); +#endif + + /* sort the protocols by protocol name */ + protocols = g_list_sort(protocols, proto_compare_name); + + /* We've assigned all the subtree type values; allocate the array + for them, and zero it out. */ + tree_is_expanded = g_new0(guint32, (num_tree_types/32)+1); +} + +void +proto_cleanup(void) +{ + /* Free the abbrev/ID GTree */ + if (gpa_name_tree) { + g_tree_destroy(gpa_name_tree); + gpa_name_tree = NULL; + } + + while (protocols) { + protocol_t *protocol = (protocol_t *)protocols->data; + header_field_info *hfinfo; + PROTO_REGISTRAR_GET_NTH(protocol->proto_id, hfinfo); + DISSECTOR_ASSERT(protocol->proto_id == hfinfo->id); + + g_slice_free(header_field_info, hfinfo); + g_list_free(protocol->fields); + protocols = g_list_remove(protocols, protocol); + g_free(protocol); + } + + if (proto_names) { + g_hash_table_destroy(proto_names); + proto_names = NULL; + } + + if (proto_short_names) { + g_hash_table_destroy(proto_short_names); + proto_short_names = NULL; + } + + if (proto_filter_names) { + g_hash_table_destroy(proto_filter_names); + proto_filter_names = NULL; + } + + if (gpa_hfinfo.allocated_len) { + gpa_hfinfo.len = 0; + gpa_hfinfo.allocated_len = 0; + g_free(gpa_hfinfo.hfi); + gpa_hfinfo.hfi = NULL; + } + g_free(tree_is_expanded); + tree_is_expanded = NULL; +} + +static gboolean +proto_tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, + gpointer data) +{ + proto_node *pnode = tree; + proto_node *child; + proto_node *current; + + if (func(pnode, data)) + return TRUE; + + child = pnode->first_child; + while (child != NULL) { + /* + * The routine we call might modify the child, e.g. by + * freeing it, so we get the child's successor before + * calling that routine. + */ + current = child; + child = current->next; + if (proto_tree_traverse_pre_order((proto_tree *)current, func, data)) + return TRUE; + } + + return FALSE; +} + +gboolean +proto_tree_traverse_post_order(proto_tree *tree, proto_tree_traverse_func func, + gpointer data) +{ + proto_node *pnode = tree; + proto_node *child; + proto_node *current; + + child = pnode->first_child; + while (child != NULL) { + /* + * The routine we call might modify the child, e.g. by + * freeing it, so we get the child's successor before + * calling that routine. + */ + current = child; + child = current->next; + if (proto_tree_traverse_post_order((proto_tree *)current, func, data)) + return TRUE; + } + if (func(pnode, data)) + return TRUE; + + return FALSE; +} + +void +proto_tree_children_foreach(proto_tree *tree, proto_tree_foreach_func func, + gpointer data) +{ + proto_node *node = tree; + proto_node *current; + + if (!node) + return; + + node = node->first_child; + while (node != NULL) { + current = node; + node = current->next; + func((proto_tree *)current, data); + } +} + +static void +free_GPtrArray_value(gpointer key, gpointer value, gpointer user_data _U_) +{ + GPtrArray *ptrs = (GPtrArray *)value; + gint hfid = (gint)(long)key; + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfid, hfinfo); + if (hfinfo->ref_type != HF_REF_TYPE_NONE) { + /* when a field is referenced by a filter this also + affects the refcount for the parent protocol so we need + to adjust the refcount for the parent as well + */ + if (hfinfo->parent != -1) { + header_field_info *parent_hfinfo; + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + parent_hfinfo->ref_type = HF_REF_TYPE_NONE; + } + hfinfo->ref_type = HF_REF_TYPE_NONE; + } + + g_ptr_array_free(ptrs, TRUE); +} + +static void +free_node_tree_data(tree_data_t *tree_data) +{ + if (tree_data->interesting_hfids) { + /* Free all the GPtrArray's in the interesting_hfids hash. */ + g_hash_table_foreach(tree_data->interesting_hfids, + free_GPtrArray_value, NULL); + + /* And then destroy the hash. */ + g_hash_table_destroy(tree_data->interesting_hfids); + } + if (tree_data->fi_tmp) + FIELD_INFO_FREE(tree_data->fi_tmp); + + /* And finally the tree_data_t itself. */ + g_free(tree_data); +} + +#define FREE_NODE_FIELD_INFO(finfo) \ + if (finfo->rep) { \ + ITEM_LABEL_FREE(finfo->rep); \ + } \ + FVALUE_CLEANUP(&finfo->value); \ + FIELD_INFO_FREE(finfo); + +static void +proto_tree_free_node(proto_node *node, gpointer data _U_) +{ + field_info *finfo = PNODE_FINFO(node); + + proto_tree_children_foreach(node, proto_tree_free_node, NULL); + + /* free the field_info data. */ + FREE_NODE_FIELD_INFO(finfo); + node->finfo = NULL; + + /* Free the proto_node. */ + PROTO_NODE_FREE(node); +} + +/* frees the resources that the dissection a proto_tree uses */ +void +proto_tree_free(proto_tree *tree) +{ + tree_data_t *tree_data = PTREE_DATA(tree); + + proto_tree_children_foreach(tree, proto_tree_free_node, NULL); + + /* free root node */ + PROTO_NODE_FREE(tree); + + /* free tree data */ + free_node_tree_data(tree_data); +} + +/* Is the parsing being done for a visible proto_tree or an invisible one? + * By setting this correctly, the proto_tree creation is sped up by not + * having to call g_vsnprintf and copy strings around. + */ +gboolean +proto_tree_set_visible(proto_tree *tree, gboolean visible) +{ + gboolean old_visible = PTREE_DATA(tree)->visible; + + PTREE_DATA(tree)->visible = visible; + + return old_visible; +} + +void +proto_tree_set_fake_protocols(proto_tree *tree, gboolean fake_protocols) +{ + PTREE_DATA(tree)->fake_protocols = fake_protocols; +} + +/* Assume dissector set only its protocol fields. + This function is called by dissectors and allows the speeding up of filtering + in wireshark; if this function returns FALSE it is safe to reset tree to NULL + and thus skip calling most of the expensive proto_tree_add_...() + functions. + If the tree is visible we implicitly assume the field is referenced. +*/ +gboolean +proto_field_is_referenced(proto_tree *tree, int proto_id) +{ + register header_field_info *hfinfo; + + + if (!tree) + return FALSE; + + if (PTREE_DATA(tree)->visible) + return TRUE; + + PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo); + if (hfinfo->ref_type != HF_REF_TYPE_NONE) + return TRUE; + + if (hfinfo->type == FT_PROTOCOL && !PTREE_DATA(tree)->fake_protocols) + return TRUE; + + return FALSE; +} + + +/* Finds a record in the hf_info_records array by id. */ +header_field_info * +proto_registrar_get_nth(guint hfindex) +{ + register header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + return hfinfo; +} + + +/* Prefix initialization + * this allows for a dissector to register a display filter name prefix + * so that it can delay the initialization of the hf array as long as + * possible. + */ + +/* compute a hash for the part before the dot of a display filter */ +static guint +prefix_hash (gconstpointer key) { + /* end the string at the dot and compute its hash */ + gchar* copy = ep_strdup((const gchar *)key); + gchar* c = copy; + + for (; *c; c++) { + if (*c == '.') { + *c = 0; + break; + } + } + + return g_str_hash(copy); +} + +/* are both strings equal up to the end or the dot? */ +static gboolean +prefix_equal (gconstpointer ap, gconstpointer bp) { + const gchar* a = (const gchar *)ap; + const gchar* b = (const gchar *)bp; + + do { + gchar ac = *a++; + gchar bc = *b++; + + if ( (ac == '.' || ac == '\0') && (bc == '.' || bc == '\0') ) return TRUE; + + if ( (ac == '.' || ac == '\0') && ! (bc == '.' || bc == '\0') ) return FALSE; + if ( (bc == '.' || bc == '\0') && ! (ac == '.' || ac == '\0') ) return FALSE; + + if (ac != bc) return FALSE; + } while (1); + + return FALSE; +} + + +/* indexed by prefix, contains initializers */ +static GHashTable* prefixes = NULL; + + +/* Register a new prefix for "delayed" initialization of field arrays */ +void +proto_register_prefix(const char *prefix, prefix_initializer_t pi ) { + if (! prefixes ) { + prefixes = g_hash_table_new(prefix_hash, prefix_equal); + } + + g_hash_table_insert(prefixes, (gpointer)prefix, pi); +} + +/* helper to call all prefix initializers */ +static gboolean +initialize_prefix(gpointer k, gpointer v, gpointer u _U_) { + ((prefix_initializer_t)v)((const char *)k); + return TRUE; +} + +/** Initialize every remaining uninitialized prefix. */ +void +proto_initialize_all_prefixes(void) { + g_hash_table_foreach_remove(prefixes, initialize_prefix, NULL); +} + +/* Finds a record in the hf_info_records array by name. + * If it fails to find it in the already registered fields, + * it tries to find and call an initializer in the prefixes + * table and if so it looks again. + */ +header_field_info * +proto_registrar_get_byname(const char *field_name) +{ + header_field_info *hfinfo; + prefix_initializer_t pi; + + if (!field_name) + return NULL; + + hfinfo = (header_field_info *)g_tree_lookup(gpa_name_tree, field_name); + + if (hfinfo) + return hfinfo; + + if (!prefixes) + return NULL; + + if ((pi = (prefix_initializer_t)g_hash_table_lookup(prefixes, field_name) ) != NULL) { + pi(field_name); + g_hash_table_remove(prefixes, field_name); + } else { + return NULL; + } + + return (header_field_info *)g_tree_lookup(gpa_name_tree, field_name); +} + +int +proto_registrar_get_id_byname(const char *field_name) +{ + header_field_info *hfinfo; + + hfinfo = proto_registrar_get_byname(field_name); + + if (!hfinfo) + return -1; + + return hfinfo->id; +} + + +static void +ptvcursor_new_subtree_levels(ptvcursor_t *ptvc) +{ + subtree_lvl *pushed_tree; + + DISSECTOR_ASSERT(ptvc->pushed_tree_max <= SUBTREE_MAX_LEVELS-SUBTREE_ONCE_ALLOCATION_NUMBER); + ptvc->pushed_tree_max += SUBTREE_ONCE_ALLOCATION_NUMBER; + + pushed_tree = (subtree_lvl *)ep_alloc(sizeof(subtree_lvl) * ptvc->pushed_tree_max); + DISSECTOR_ASSERT(pushed_tree != NULL); + if (ptvc->pushed_tree) + memcpy(pushed_tree, ptvc->pushed_tree, ptvc->pushed_tree_max - SUBTREE_ONCE_ALLOCATION_NUMBER); + ptvc->pushed_tree = pushed_tree; +} + +static void +ptvcursor_free_subtree_levels(ptvcursor_t *ptvc) +{ + ptvc->pushed_tree = NULL; + ptvc->pushed_tree_max = 0; + DISSECTOR_ASSERT(ptvc->pushed_tree_index == 0); + ptvc->pushed_tree_index = 0; +} + +/* Allocates an initializes a ptvcursor_t with 3 variables: + * proto_tree, tvbuff, and offset. */ +ptvcursor_t * +ptvcursor_new(proto_tree *tree, tvbuff_t *tvb, gint offset) +{ + ptvcursor_t *ptvc; + + ptvc = (ptvcursor_t *)ep_alloc(sizeof(ptvcursor_t)); + ptvc->tree = tree; + ptvc->tvb = tvb; + ptvc->offset = offset; + ptvc->pushed_tree = NULL; + ptvc->pushed_tree_max = 0; + ptvc->pushed_tree_index = 0; + return ptvc; +} + + +/* Frees memory for ptvcursor_t, but nothing deeper than that. */ +void +ptvcursor_free(ptvcursor_t *ptvc) +{ + ptvcursor_free_subtree_levels(ptvc); + /*g_free(ptvc);*/ +} + +/* Returns tvbuff. */ +tvbuff_t * +ptvcursor_tvbuff(ptvcursor_t *ptvc) +{ + return ptvc->tvb; +} + +/* Returns current offset. */ +gint +ptvcursor_current_offset(ptvcursor_t *ptvc) +{ + return ptvc->offset; +} + +proto_tree * +ptvcursor_tree(ptvcursor_t *ptvc) +{ + if (!ptvc) + return NULL; + + return ptvc->tree; +} + +void +ptvcursor_set_tree(ptvcursor_t *ptvc, proto_tree *tree) +{ + ptvc->tree = tree; +} + +/* creates a subtree, sets it as the working tree and pushes the old working tree */ +proto_tree * +ptvcursor_push_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) +{ + subtree_lvl *subtree; + if (ptvc->pushed_tree_index >= ptvc->pushed_tree_max) + ptvcursor_new_subtree_levels(ptvc); + + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index; + subtree->tree = ptvc->tree; + subtree->it= NULL; + ptvc->pushed_tree_index++; + return ptvcursor_set_subtree(ptvc, it, ett_subtree); +} + +/* pops a subtree */ +void +ptvcursor_pop_subtree(ptvcursor_t *ptvc) +{ + subtree_lvl *subtree; + + if (ptvc->pushed_tree_index <= 0) + return; + + ptvc->pushed_tree_index--; + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index; + if (subtree->it != NULL) + proto_item_set_len(subtree->it, ptvcursor_current_offset(ptvc) - subtree->cursor_offset); + + ptvc->tree = subtree->tree; +} + +/* saves the current tvb offset and the item in the current subtree level */ +static void +ptvcursor_subtree_set_item(ptvcursor_t *ptvc, proto_item *it) +{ + subtree_lvl *subtree; + + DISSECTOR_ASSERT(ptvc->pushed_tree_index > 0); + + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index - 1; + subtree->it = it; + subtree->cursor_offset = ptvcursor_current_offset(ptvc); +} + +/* Creates a subtree and adds it to the cursor as the working tree but does not + * save the old working tree */ +proto_tree * +ptvcursor_set_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) +{ + ptvc->tree = proto_item_add_subtree(it, ett_subtree); + return ptvc->tree; +} + +static proto_tree * +ptvcursor_add_subtree_item(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree, gint length) +{ + ptvcursor_push_subtree(ptvc, it, ett_subtree); + if (length == SUBTREE_UNDEFINED_LENGTH) + ptvcursor_subtree_set_item(ptvc, it); + return ptvcursor_tree(ptvc); +} + +/* Add an item to the tree and create a subtree + * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. + * In this case, when the subtree will be closed, the parent item length will + * be equal to the advancement of the cursor since the creation of the subtree. + */ +proto_tree * +ptvcursor_add_with_subtree(ptvcursor_t *ptvc, int hfindex, gint length, + const guint encoding, gint ett_subtree) +{ + proto_item *it; + + it = ptvcursor_add_no_advance(ptvc, hfindex, length, encoding); + return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length); +} + +static proto_item * +proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length); + +/* Add a text node to the tree and create a subtree + * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. + * In this case, when the subtree will be closed, the item length will be equal + * to the advancement of the cursor since the creation of the subtree. + */ +proto_tree * +ptvcursor_add_text_with_subtree(ptvcursor_t *ptvc, gint length, + gint ett_subtree, const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + proto_tree *tree; + + tree = ptvcursor_tree(ptvc); + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, ptvcursor_tvbuff(ptvc), + ptvcursor_current_offset(ptvc), length); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return ptvcursor_add_subtree_item(ptvc, pi, ett_subtree, length); +} + +/* Add a text-only node, leaving it to our caller to fill the text in */ +static proto_item * +proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length) +{ + proto_item *pi; + field_info *new_fi; + + if (tree == NULL) + return NULL; + + pi = proto_tree_add_pi(tree, &hfi_text_only, tvb, start, &length, &new_fi); + + return pi; +} + +/* Add a text-only node to the proto_tree */ +proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, tvb, start, length); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Add a text-only node to the proto_tree (va_list version) */ +proto_item * +proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start, + gint length, const char *format, va_list ap) +{ + proto_item *pi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, tvb, start, length); + + TRY_TO_FAKE_THIS_REPR(pi); + + proto_tree_set_representation(pi, format, ap); + + return pi; +} + +/* Add a text-only node for debugging purposes. The caller doesn't need + * to worry about tvbuff, start, or length. Debug message gets sent to + * STDOUT, too */ +proto_item * +proto_tree_add_debug_text(proto_tree *tree, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_text_node(tree, NULL, 0, 0); + + if (pi) { + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + printf("\n"); + + return pi; +} + +/* We could probably get away with changing is_error to a minimum length value. */ +static void +report_type_length_mismatch(proto_tree *tree, const gchar *descr, int length, gboolean is_error) { + tree_data_t *tree_data = PTREE_DATA(tree); + field_info *fi_save = tree_data->fi_tmp; + + /* Keep the current item from getting freed by proto_tree_new_item. */ + tree_data->fi_tmp = NULL; + + expert_add_info_format(NULL, tree, PI_MALFORMED, is_error ? PI_ERROR : PI_WARN, "Trying to fetch %s with length %d", descr, length); + + tree_data->fi_tmp = fi_save; + + if (is_error) { + THROW(ReportedBoundsError); + } +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static guint32 +get_uint_value(proto_tree *tree, tvbuff_t *tvb, gint offset, gint length, const guint encoding) +{ + guint32 value; + gboolean length_error; + + switch (length) { + + case 1: + value = tvb_get_guint8(tvb, offset); + break; + + case 2: + value = encoding ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset); + break; + + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + break; + + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + if (length < 1) { + length_error = TRUE; + value = 0; + } else { + length_error = FALSE; + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + } + report_type_length_mismatch(tree, "an unsigned integer", length, length_error); + break; + } + return value; +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static gint32 +get_int_value(proto_tree *tree, tvbuff_t *tvb, gint offset, gint length, const guint encoding) +{ + gint32 value; + gboolean length_error; + + switch (length) { + + case 1: + value = (gint8)tvb_get_guint8(tvb, offset); + break; + + case 2: + value = (gint16) (encoding ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset)); + break; + + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + if (value & 0x00800000) { + /* Sign bit is set; sign-extend it. */ + value |= 0xFF000000; + } + break; + + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + if (length < 1) { + length_error = TRUE; + value = 0; + } else { + length_error = FALSE; + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + } + report_type_length_mismatch(tree, "a signed integer", length, length_error); + break; + } + return value; +} + +static void +tree_data_add_maybe_interesting_field(tree_data_t *tree_data, field_info *fi) +{ + const header_field_info *hfinfo = fi->hfinfo; + + if (hfinfo->ref_type == HF_REF_TYPE_DIRECT) { + GPtrArray *ptrs = NULL; + + if (tree_data->interesting_hfids == NULL) { + /* Initialize the hash because we now know that it is needed */ + tree_data->interesting_hfids = + g_hash_table_new(g_direct_hash, NULL /* g_direct_equal */); + } else + ptrs = (GPtrArray *)g_hash_table_lookup(tree_data->interesting_hfids, + GINT_TO_POINTER(hfinfo->id)); + if (!ptrs) { + /* First element triggers the creation of pointer array */ + ptrs = g_ptr_array_new(); + g_hash_table_insert(tree_data->interesting_hfids, + GINT_TO_POINTER(hfinfo->id), ptrs); + } + + g_ptr_array_add(ptrs, fi); + } +} + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +static proto_item * +proto_tree_new_item(field_info *new_fi, proto_tree *tree, + tvbuff_t *tvb, gint start, gint length, + guint encoding) +{ + tree_data_t *tree_data = PTREE_DATA(tree); + proto_item *pi; + guint32 value, n; + float floatval; + double doubleval; + const char *string; + nstime_t time_stamp; + guint32 tmpsecs; + gboolean length_error; + + /* there is a possibility here that we might raise an exception + * and thus would lose track of the field_info. + * store it in a temp so that if we come here again we can reclaim + * the field_info without leaking memory. + */ + if (tree_data->fi_tmp) { + /* oops, last one we got must have been lost due + * to an exception. + * good thing we saved it, now we can reverse the + * memory leak and reclaim it. + */ + FIELD_INFO_FREE(tree_data->fi_tmp); + } + /* we might throw an exception, keep track of this one + * across the "dangerous" section below. + */ + tree_data->fi_tmp = new_fi; + + switch (new_fi->hfinfo->type) { + case FT_NONE: + /* no value to set for FT_NONE */ + break; + + case FT_PROTOCOL: + proto_tree_set_protocol_tvb(new_fi, tvb); + break; + + case FT_BYTES: + proto_tree_set_bytes_tvb(new_fi, tvb, start, length); + break; + + case FT_UINT_BYTES: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + n = get_uint_value(tree, tvb, start, length, encoding); + proto_tree_set_bytes_tvb(new_fi, tvb, start + length, n); + + /* Instead of calling proto_item_set_len(), since we don't yet + * have a proto_item, we set the field_info's length ourselves. */ + new_fi->length = n + length; + break; + + case FT_BOOLEAN: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_boolean(new_fi, + get_uint_value(tree, tvb, start, length, encoding)); + break; + + /* XXX - make these just FT_UINT? */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_uint(new_fi, + get_uint_value(tree, tvb, start, length, encoding)); + break; + + case FT_INT64: + case FT_UINT64: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length < 1 || length > 8) { + length_error = length < 1 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a 64-bit integer", length, length_error); + } + proto_tree_set_uint64_tvb(new_fi, tvb, start, length, encoding); + break; + + /* XXX - make these just FT_INT? */ + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_int(new_fi, + get_int_value(tree, tvb, start, length, encoding)); + break; + + case FT_IPv4: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_IPv4_LEN) { + length_error = length < FT_IPv4_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPv4 address", length, length_error); + } + value = tvb_get_ipv4(tvb, start); + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + */ + proto_tree_set_ipv4(new_fi, encoding ? GUINT32_SWAP_LE_BE(value) : value); + break; + + case FT_IPXNET: + if (length != FT_IPXNET_LEN) { + length_error = length < FT_IPXNET_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPXNET address", length, length_error); + } + proto_tree_set_ipxnet(new_fi, + get_uint_value(tree, tvb, start, FT_IPXNET_LEN, FALSE)); + break; + + case FT_IPv6: + if (length != FT_IPv6_LEN) { + length_error = length < FT_IPv6_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPv6 address", length, length_error); + } + proto_tree_set_ipv6_tvb(new_fi, tvb, start, length); + break; + + case FT_AX25: + if (length != 7) { + length_error = length < 7 ? TRUE : FALSE; + expert_add_info_format(NULL, tree, PI_MALFORMED, PI_ERROR, "Trying to fetch an AX.25 address with length %d", length); + THROW(ReportedBoundsError); + } + proto_tree_set_ax25_tvb(new_fi, tvb, start); + break; + + case FT_ETHER: + if (length != FT_ETHER_LEN) { + length_error = length < FT_ETHER_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an Ethernet", length, length_error); + } + proto_tree_set_ether_tvb(new_fi, tvb, start); + break; + + case FT_EUI64: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_EUI64_LEN) { + length_error = length < FT_EUI64_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an EUI-64 address", length, length_error); + } + proto_tree_set_eui64_tvb(new_fi, tvb, start, encoding); + break; + case FT_GUID: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_GUID_LEN) { + length_error = length < FT_GUID_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "a GUID", length, length_error); + } + proto_tree_set_guid_tvb(new_fi, tvb, start, encoding); + break; + + case FT_OID: + proto_tree_set_oid_tvb(new_fi, tvb, start, length); + break; + + case FT_FLOAT: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + * + * At some point in the future, we might + * support non-IEEE-binary floating-point + * formats in the encoding as well + * (IEEE decimal, System/3x0, VAX). + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a single-precision floating point number", length, length_error); + } + if (encoding) + floatval = tvb_get_letohieee_float(tvb, start); + else + floatval = tvb_get_ntohieee_float(tvb, start); + proto_tree_set_float(new_fi, floatval); + break; + + case FT_DOUBLE: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + * + * At some point in the future, we might + * support non-IEEE-binary floating-point + * formats in the encoding as well + * (IEEE decimal, System/3x0, VAX). + */ + if (encoding == TRUE) + encoding = ENC_LITTLE_ENDIAN; + if (length != 8) { + length_error = length < 8 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a double-precision floating point number", length, length_error); + } + if (encoding) + doubleval = tvb_get_letohieee_double(tvb, start); + else + doubleval = tvb_get_ntohieee_double(tvb, start); + proto_tree_set_double(new_fi, doubleval); + break; + + case FT_STRING: + proto_tree_set_string_tvb(new_fi, tvb, start, length, + encoding); + break; + + case FT_STRINGZ: + if (length < -1 ) { + report_type_length_mismatch(tree, "a string", length, TRUE); + } + /* Instead of calling proto_item_set_len(), + * since we don't yet have a proto_item, we + * set the field_info's length ourselves. + * + * XXX - our caller can't use that length to + * advance an offset unless they arrange that + * there always be a protocol tree into which + * we're putting this item. + */ + if (length == -1) { + /* This can throw an exception */ + string = tvb_get_stringz_enc(tvb, start, &length, encoding); + } else if (length == 0) { + string = "[Empty]"; + } else { + /* In this case, length signifies + * the length of the string. + * + * This could either be a null-padded + * string, which doesn't necessarily + * have a '\0' at the end, or a + * null-terminated string, with a + * trailing '\0'. (Yes, there are + * cases where you have a string + * that's both counted and null- + * terminated.) + * + * In the first case, we must + * allocate a buffer of length + * "length+1", to make room for + * a trailing '\0'. + * + * In the second case, we don't + * assume that there is a trailing + * '\0' there, as the packet might + * be malformed. (XXX - should we + * throw an exception if there's no + * trailing '\0'?) Therefore, we + * allocate a buffer of length + * "length+1", and put in a trailing + * '\0', just to be safe. + * + * (XXX - this would change if + * we made string values counted + * rather than null-terminated.) + */ + string = tvb_get_ephemeral_string_enc(tvb, start, length, encoding); + } + new_fi->length = length; + proto_tree_set_string(new_fi, string); + break; + + case FT_UINT_STRING: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", if the + * encoding value is TRUE, treat that as + * ASCII with a little-endian length. + * + * This won't work for code that passes + * arbitrary non-zero values; that code + * will need to be fixed. + */ + if (encoding == TRUE) + encoding = ENC_ASCII|ENC_LITTLE_ENDIAN; + n = get_uint_value(tree, tvb, start, length, encoding & ~ENC_CHARENCODING_MASK); + proto_tree_set_string_tvb(new_fi, tvb, start + length, n, + encoding); + + /* Instead of calling proto_item_set_len(), since we + * don't yet have a proto_item, we set the + * field_info's length ourselves. + * + * XXX - our caller can't use that length to + * advance an offset unless they arrange that + * there always be a protocol tree into which + * we're putting this item. + */ + new_fi->length = n + length; + break; + + case FT_ABSOLUTE_TIME: + /* + * Absolute times can be in any of a number of + * formats, and they can be big-endian or + * little-endian. + * + * Historically FT_TIMEs were only timespecs; + * the only question was whether they were stored + * in big- or little-endian format. + * + * For backwards compatibility, we interpret an + * encoding of 1 as meaning "little-endian timespec", + * so that passing TRUE is interpreted as that. + */ + if (encoding == TRUE) + encoding = ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN; + + if (length != 8 && length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "an absolute time value", length, length_error); + } + + switch (encoding) { + + case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both big-endian. + */ + time_stamp.secs = tvb_get_ntohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_ntohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both little-endian. + */ + time_stamp.secs = tvb_get_letohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_letohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_NTP|ENC_BIG_ENDIAN: + /* + * NTP time stamp, big-endian. + */ + +/* XXX - where should this go? */ +#define NTP_BASETIME 2208988800ul + + /* We need a temporary variable here so the unsigned math + * works correctly (for years > 2036 according to RFC 2030 + * chapter 3). + */ + tmpsecs = tvb_get_ntohl(tvb, start); + if (tmpsecs) + time_stamp.secs = tmpsecs - (guint32)NTP_BASETIME; + else + time_stamp.secs = tmpsecs; /* 0 */ + + if (length == 8) { + /* + * We're using nanoseconds here (and we will + * display nanoseconds), but NTP's timestamps + * have a precision in microseconds or greater. + * Round to 1 microsecond. + */ + time_stamp.nsecs = (int)(1000000*(tvb_get_ntohl(tvb, start+4)/4294967296.0)); + time_stamp.nsecs *= 1000; + } else { + time_stamp.nsecs = 0; + } + break; + + case ENC_TIME_NTP|ENC_LITTLE_ENDIAN: + /* + * NTP time stamp, big-endian. + */ + tmpsecs = tvb_get_letohl(tvb, start); + if (tmpsecs) + time_stamp.secs = tmpsecs - (guint32)NTP_BASETIME; + else + time_stamp.secs = tmpsecs; /* 0 */ + + if (length == 8) { + /* + * We're using nanoseconds here (and we will + * display nanoseconds), but NTP's timestamps + * have a precision in microseconds or greater. + * Round to 1 microsecond. + */ + time_stamp.nsecs = (int)(1000000*(tvb_get_letohl(tvb, start+4)/4294967296.0)); + time_stamp.nsecs *= 1000; + } else { + time_stamp.nsecs = 0; + } + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + time_stamp.secs = 0; + time_stamp.nsecs = 0; + break; + } + proto_tree_set_time(new_fi, &time_stamp); + break; + + case FT_RELATIVE_TIME: + /* + * Relative times can be in any of a number of + * formats, and they can be big-endian or + * little-endian. + * + * Historically FT_TIMEs were only timespecs; + * the only question was whether they were stored + * in big- or little-endian format. + * + * For backwards compatibility, we interpret an + * encoding of 1 as meaning "little-endian timespec", + * so that passing TRUE is interpreted as that. + */ + if (encoding == TRUE) + encoding = ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN; + switch (encoding) { + + if (length != 8 && length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a relative time value", length, length_error); + } + + case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both big-endian. + */ + time_stamp.secs = tvb_get_ntohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_ntohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both little-endian. + */ + time_stamp.secs = tvb_get_letohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_letohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + } + proto_tree_set_time(new_fi, &time_stamp); + break; + + default: + g_error("new_fi->hfinfo->type %d (%s) not handled\n", + new_fi->hfinfo->type, + ftype_name(new_fi->hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + FI_SET_FLAG(new_fi, (encoding & ENC_LITTLE_ENDIAN) ? FI_LITTLE_ENDIAN : FI_BIG_ENDIAN); + + /* Don't add new node to proto_tree until now so that any exceptions + * raised by a tvbuff access method doesn't leave junk in the proto_tree. */ + /* XXX. wouldn't be better to add this item to tree, with some special flag (FI_EXCEPTION?) + * to know which item caused exception? */ + pi = proto_tree_add_node(tree, new_fi); + + /* we did not raise an exception so we dont have to remember this + * field_info struct any more. + */ + tree_data->fi_tmp = NULL; + + return pi; +} + +/* Gets data from tvbuff, adds it to proto_tree, increments offset, + and returns proto_item* */ +proto_item * +ptvcursor_add(ptvcursor_t *ptvc, int hfindex, gint length, + const guint encoding) +{ + field_info *new_fi; + header_field_info *hfinfo; + gint item_length; + guint32 n; + int offset; + + /* We can't fake it just yet. We have to advance the cursor + TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo); */ + + offset = ptvc->offset; + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + get_hfi_and_length(hfinfo, ptvc->tvb, offset, &length, &item_length); + ptvc->offset += length; + if (hfinfo->type == FT_UINT_BYTES || hfinfo->type == FT_UINT_STRING) { + /* + * The length of the rest of the item is in the first N + * bytes of the item. + */ + n = get_uint_value(ptvc->tree, ptvc->tvb, offset, length, encoding); + ptvc->offset += n; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo); + + new_fi = new_field_info(ptvc->tree, hfinfo, ptvc->tvb, offset, item_length); + + return proto_tree_new_item(new_fi, ptvc->tree, ptvc->tvb, + offset, length, encoding); +} + +/* + * Validates that field length bytes are available starting from + * start (pos/neg). Throws an exception if they aren't. + */ +static void +test_length(header_field_info *hfinfo, proto_tree *tree, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian) +{ + gint size = length; + + if (!tvb) + return; + + if (hfinfo->type == FT_UINT_BYTES || hfinfo->type == FT_UINT_STRING) { + guint32 n; + + n = get_uint_value(tree, tvb, start, length, little_endian); + if (n > size + n) { + /* If n > size + n then we have an integer overflow, so + * set size to -1, which will force the + * tvb_ensure_bytes_exist call below to throw a + * ReportedBoundsError + */ + size = -1; + } + else { + size += n; + } + } + tvb_ensure_bytes_exist(tvb, start, size); +} + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +proto_item * +proto_tree_add_item(proto_tree *tree, const int hfindex, tvbuff_t *tvb, + const gint start, gint length, const guint encoding) +{ + field_info *new_fi; + header_field_info *hfinfo; + gint item_length; + + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + get_hfi_and_length(hfinfo, tvb, start, &length, &item_length); + test_length(hfinfo, tree, tvb, start, item_length, encoding); + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + new_fi = new_field_info(tree, hfinfo, tvb, start, item_length); + + if (new_fi == NULL) + return NULL; + + return proto_tree_new_item(new_fi, tree, tvb, start, length, encoding); +} + +/* Add a FT_NONE to a proto_tree */ +proto_item * +proto_tree_add_none_format(proto_tree *tree, const int hfindex, tvbuff_t *tvb, + const gint start, gint length, const char *format, + ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + field_info *new_fi; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_NONE); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + /* no value to set for FT_NONE */ + return pi; +} + +/* Gets data from tvbuff, adds it to proto_tree, *DOES NOT* increment + * offset, and returns proto_item* */ +proto_item * +ptvcursor_add_no_advance(ptvcursor_t* ptvc, int hf, gint length, + const guint encoding) +{ + proto_item *item; + + item = proto_tree_add_item(ptvc->tree, hf, ptvc->tvb, ptvc->offset, + length, encoding); + + return item; +} + +/* Advance the ptvcursor's offset within its tvbuff without + * adding anything to the proto_tree. */ +void +ptvcursor_advance(ptvcursor_t* ptvc, gint length) +{ + ptvc->offset += length; +} + + +static void +proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb) +{ + fvalue_set(&fi->value, tvb, TRUE); +} + +/* Add a FT_PROTOCOL to a proto_tree */ +proto_item * +proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char *format, ...) +{ + proto_item *pi; + va_list ap; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_PROTOCOL); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + + proto_tree_set_protocol_tvb(new_fi, (start == 0 ? tvb : tvb_new_subset(tvb, start, length, length))); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + + +/* Add a FT_BYTES to a proto_tree */ +proto_item * +proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8 *start_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_BYTES); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_bytes(new_fi, start_ptr, length); + + return pi; +} + +proto_item * +proto_tree_add_bytes_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8 *start_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + if (start_ptr) + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + start_ptr); + else + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + tvb_get_ptr(tvb, start, length)); + + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + + return pi; +} + +proto_item * +proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8 *start_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + if (start_ptr) + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + start_ptr); + else + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + tvb_get_ptr(tvb, start, length)); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length) +{ + GByteArray *bytes; + + bytes = g_byte_array_new(); + if (length > 0) { + g_byte_array_append(bytes, start_ptr, length); + } + fvalue_set(&fi->value, bytes, TRUE); +} + + +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length) +{ + proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length); +} + +/* Add a FT_*TIME to a proto_tree */ +proto_item * +proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, nstime_t *value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_ABSOLUTE_TIME || + hfinfo->type == FT_RELATIVE_TIME); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_time(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_time_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, nstime_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, nstime_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_*TIME value */ +static void +proto_tree_set_time(field_info *fi, nstime_t *value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + + fvalue_set(&fi->value, value_ptr, FALSE); +} + +/* Add a FT_IPXNET to a proto_tree */ +proto_item * +proto_tree_add_ipxnet(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPXNET); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipxnet(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPXNET value */ +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value) +{ + fvalue_set_uinteger(&fi->value, value); +} + +/* Add a FT_IPv4 to a proto_tree */ +proto_item * +proto_tree_add_ipv4(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPv4); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipv4(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipv4_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPv4 value */ +static void +proto_tree_set_ipv4(field_info *fi, guint32 value) +{ + fvalue_set_uinteger(&fi->value, value); +} + +/* Add a FT_IPv6 to a proto_tree */ +proto_item * +proto_tree_add_ipv6(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPv6); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipv6(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_ipv6_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPv6 value */ +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + fvalue_set(&fi->value, (gpointer) value_ptr, FALSE); +} + +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) +{ + proto_tree_set_ipv6(fi, tvb_get_ptr(tvb, start, length)); +} + +/* Add a FT_GUID to a proto_tree */ +proto_item * +proto_tree_add_guid(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const e_guid_t *value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_GUID); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_guid(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_guid_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const e_guid_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_guid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_guid_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const e_guid_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_guid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_GUID value */ +static void +proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + fvalue_set(&fi->value, (gpointer) value_ptr, FALSE); +} + +static void +proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start, + const guint encoding) +{ + e_guid_t guid; + + tvb_get_guid(tvb, start, &guid, encoding); + proto_tree_set_guid(fi, &guid); +} + +/* Add a FT_OID to a proto_tree */ +proto_item * +proto_tree_add_oid(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_OID); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_oid(new_fi, value_ptr, length); + + return pi; +} + +proto_item * +proto_tree_add_oid_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_oid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_oid_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_oid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_OID value */ +static void +proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length) +{ + GByteArray *bytes; + + DISSECTOR_ASSERT(value_ptr != NULL); + + bytes = g_byte_array_new(); + if (length > 0) { + g_byte_array_append(bytes, value_ptr, length); + } + fvalue_set(&fi->value, bytes, TRUE); +} + +static void +proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) +{ + proto_tree_set_oid(fi, tvb_get_ptr(tvb, start, length), length); +} + +static void +proto_tree_set_uint64(field_info *fi, guint64 value) +{ + fvalue_set_integer64(&fi->value, value); +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static void +proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, + guint length, const guint encoding) +{ + guint64 value = 0; + guint8* b = (guint8 *)ep_tvb_memdup(tvb, start, length); + + if (encoding) { + b += length; + switch (length) { + default: DISSECTOR_ASSERT_NOT_REACHED(); + case 8: value <<= 8; value += *--b; + case 7: value <<= 8; value += *--b; + case 6: value <<= 8; value += *--b; + case 5: value <<= 8; value += *--b; + case 4: value <<= 8; value += *--b; + case 3: value <<= 8; value += *--b; + case 2: value <<= 8; value += *--b; + case 1: value <<= 8; value += *--b; + break; + } + } else { + switch (length) { + default: DISSECTOR_ASSERT_NOT_REACHED(); + case 8: value <<= 8; value += *b++; + case 7: value <<= 8; value += *b++; + case 6: value <<= 8; value += *b++; + case 5: value <<= 8; value += *b++; + case 4: value <<= 8; value += *b++; + case 3: value <<= 8; value += *b++; + case 2: value <<= 8; value += *b++; + case 1: value <<= 8; value += *b++; + break; + } + } + + proto_tree_set_uint64(fi, value); +} + +/* Add a FT_STRING or FT_STRINGZ to a proto_tree. Creates own copy of string, + * and frees it when the proto_tree is destroyed. */ +proto_item * +proto_tree_add_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + DISSECTOR_ASSERT(length >= 0); + proto_tree_set_string(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_unicode_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + DISSECTOR_ASSERT(g_utf8_validate(value, -1, NULL)); + return proto_tree_add_string_format_value(tree, hfindex, tvb, start, length, value, "%s", value); +} + +proto_item * +proto_tree_add_string_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char* value, + const char *format, + ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Appends string data to a FT_STRING or FT_STRINGZ, allowing progressive + * field info update instead of only updating the representation as does + * proto_item_append_text() + */ +/* NOTE: this function will break with the TRY_TO_FAKE_THIS_ITEM() + * speed optimization. + * Currently only WSP use this function so it is not that bad but try to + * avoid using this one if possible. + * IF you must use this function you MUST also disable the + * TRY_TO_FAKE_THIS_ITEM() optimization for your dissector/function + * using proto_item_append_string(). + * Do that by faking that the tree is visible by calling + * proto_tree_set_visible(tree, TRUE) (see packet-wsp.c) + * BEFORE you create the item you are later going to use + * proto_item_append_string() on. + */ +void +proto_item_append_string(proto_item *pi, const char *str) +{ + field_info *fi; + header_field_info *hfinfo; + gchar *old_str, *new_str; + + if (!pi) + return; + if (!*str) + return; + + fi = PITEM_FINFO(pi); + DISSECTOR_ASSERT_HINT(fi, "proto_tree_set_visible(tree, TRUE) should have been called previously"); + + hfinfo = fi->hfinfo; + if (hfinfo->type == FT_PROTOCOL) { + /* TRY_TO_FAKE_THIS_ITEM() speed optimization: silently skip */ + return; + } + DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); + old_str = (guint8 *)fvalue_get(&fi->value); + new_str = ep_strdup_printf("%s%s", old_str, str); + fvalue_set(&fi->value, new_str, FALSE); +} + +/* Set the FT_STRING value */ +static void +proto_tree_set_string(field_info *fi, const char* value) +{ + if (value) { + fvalue_set(&fi->value, (gpointer) value, FALSE); + } else { + fvalue_set(&fi->value, (gpointer) "[ Null ]", FALSE); + } +} + +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length, gint encoding) +{ + gchar *string; + + if (length == -1) { + length = tvb_ensure_length_remaining(tvb, start); + } + + string = tvb_get_ephemeral_string_enc(tvb, start, length, encoding); + proto_tree_set_string(fi, string); +} + + +/* Add a FT_AX25 to a proto_tree */ +proto_item * +proto_tree_add_ax25(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_AX25); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ax25(new_fi, value); + + return pi; +} + +/* Set the FT_AX25 value */ +static void +proto_tree_set_ax25(field_info *fi, const guint8* value) +{ + fvalue_set(&fi->value, (gpointer) value, FALSE); +} + +static void +proto_tree_set_ax25_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + proto_tree_set_ax25(fi, tvb_get_ptr(tvb, start, 7)); +} + + +/* Add a FT_ETHER to a proto_tree */ +proto_item * +proto_tree_add_ether(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_ETHER); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ether(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ether_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_ETHER value */ +static void +proto_tree_set_ether(field_info *fi, const guint8* value) +{ + fvalue_set(&fi->value, (gpointer) value, FALSE); +} + +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + proto_tree_set_ether(fi, tvb_get_ptr(tvb, start, FT_ETHER_LEN)); +} + +/* Add a FT_BOOLEAN to a proto_tree */ +proto_item * +proto_tree_add_boolean(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_BOOLEAN); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_boolean(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_boolean_format_value(proto_tree *tree, int hfindex, + tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_BOOLEAN value */ +static void +proto_tree_set_boolean(field_info *fi, guint32 value) +{ + proto_tree_set_uint(fi, value); +} + +/* Add a FT_FLOAT to a proto_tree */ +proto_item * +proto_tree_add_float(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, float value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_FLOAT); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_float(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_float_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, float value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_float_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, float value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_FLOAT value */ +static void +proto_tree_set_float(field_info *fi, float value) +{ + fvalue_set_floating(&fi->value, value); +} + +/* Add a FT_DOUBLE to a proto_tree */ +proto_item * +proto_tree_add_double(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_DOUBLE); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_double(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_double_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, double value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, double value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_DOUBLE value */ +static void +proto_tree_set_double(field_info *fi, double value) +{ + fvalue_set_floating(&fi->value, value); +} + +/* Add FT_UINT{8,16,24,32} to a proto_tree */ +proto_item * +proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + switch (hfinfo->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, + &new_fi); + proto_tree_set_uint(new_fi, value); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + + return pi; +} + +proto_item * +proto_tree_add_uint_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_UINT{8,16,24,32} value */ +static void +proto_tree_set_uint(field_info *fi, guint32 value) +{ + header_field_info *hfinfo; + guint32 integer; + + hfinfo = fi->hfinfo; + integer = value; + + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + integer &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + integer >>= hfinfo->bitshift; + } + } + + fvalue_set_uinteger(&fi->value, integer); +} + +/* Add FT_UINT64 to a proto_tree */ +proto_item * +proto_tree_add_uint64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint64 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_UINT64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_uint64(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_uint64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_uint64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Add FT_INT{8,16,24,32} to a proto_tree */ +proto_item * +proto_tree_add_int(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + switch (hfinfo->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, + &new_fi); + proto_tree_set_int(new_fi, value); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + + return pi; +} + +proto_item * +proto_tree_add_int_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_INT{8,16,24,32} value */ +static void +proto_tree_set_int(field_info *fi, gint32 value) +{ + header_field_info *hfinfo; + guint32 integer; + + hfinfo = fi->hfinfo; + integer = (guint32) value; + + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + integer &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + integer >>= hfinfo->bitshift; + } + } + + fvalue_set_sinteger(&fi->value, integer); +} + +/* Add FT_INT64 to a proto_tree */ +proto_item * +proto_tree_add_int64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint64 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_INT64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_uint64(new_fi, (guint64)value); + + return pi; +} + +proto_item * +proto_tree_add_int64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_int64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} +/* Add a FT_EUI64 to a proto_tree */ +proto_item * +proto_tree_add_eui64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint64 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_EUI64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_eui64(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_eui64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_eui64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_eui64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_eui64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_EUI64 value */ +static void +proto_tree_set_eui64(field_info *fi, const guint64 value) +{ + fvalue_set_integer64(&fi->value, value); +} +static void +proto_tree_set_eui64_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding) +{ + if (encoding) + { + proto_tree_set_eui64(fi, tvb_get_letoh64(tvb, start)); + } else { + proto_tree_set_eui64(fi, tvb_get_ntoh64(tvb, start)); + } +} + +/* Add a field_info struct to the proto_tree, encapsulating it in a proto_node */ +static proto_item * +proto_tree_add_node(proto_tree *tree, field_info *fi) +{ + proto_node *pnode, *tnode, *sibling; + field_info *tfi; + + /* + * Make sure "tree" is ready to have subtrees under it, by + * checking whether it's been given an ett_ value. + * + * "PNODE_FINFO(tnode)" may be null; that's the case for the root + * node of the protocol tree. That node is not displayed, + * so it doesn't need an ett_ value to remember whether it + * was expanded. + */ + tnode = tree; + tfi = PNODE_FINFO(tnode); + if (tfi != NULL && (tfi->tree_type < 0 || tfi->tree_type >= num_tree_types)) { + REPORT_DISSECTOR_BUG(ep_strdup_printf("\"%s\" - \"%s\" tfi->tree_type: %u invalid (%s:%u)", + fi->hfinfo->name, fi->hfinfo->abbrev, tfi->tree_type, __FILE__, __LINE__)); + /* XXX - is it safe to continue here? */ + } + + PROTO_NODE_NEW(pnode); + pnode->parent = tnode; + PNODE_FINFO(pnode) = fi; + pnode->tree_data = PTREE_DATA(tree); + + if (tnode->last_child != NULL) { + sibling = tnode->last_child; + DISSECTOR_ASSERT(sibling->next == NULL); + sibling->next = pnode; + } else + tnode->first_child = pnode; + tnode->last_child = pnode; + + tree_data_add_maybe_interesting_field(pnode->tree_data, fi); + + return (proto_item *)pnode; +} + + +/* Generic way to allocate field_info and add to proto_tree. + * Sets *pfi to address of newly-allocated field_info struct */ +static proto_item * +proto_tree_add_pi(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, gint start, + gint *length, field_info **pfi) +{ + proto_item *pi; + field_info *fi; + + fi = alloc_field_info(tree, hfinfo, tvb, start, length); + pi = proto_tree_add_node(tree, fi); + + *pfi = fi; + + return pi; +} + + +static void +get_hfi_and_length(header_field_info *hfinfo, tvbuff_t *tvb, const gint start, gint *length, + gint *item_length) +{ + gint length_remaining; + + /* + * We only allow a null tvbuff if the item has a zero length, + * i.e. if there's no data backing it. + */ + DISSECTOR_ASSERT(tvb != NULL || *length == 0); + + /* + * XXX - in some protocols, there are 32-bit unsigned length + * fields, so lengths in protocol tree and tvbuff routines + * should really be unsigned. We should have, for those + * field types for which "to the end of the tvbuff" makes sense, + * additional routines that take no length argument and + * add fields that run to the end of the tvbuff. + */ + if (*length == -1) { + /* + * For FT_NONE, FT_PROTOCOL, FT_BYTES, and FT_STRING fields, + * a length of -1 means "set the length to what remains in + * the tvbuff". + * + * The assumption is either that + * + * 1) the length of the item can only be determined + * by dissection (typically true of items with + * subitems, which are probably FT_NONE or + * FT_PROTOCOL) + * + * or + * + * 2) if the tvbuff is "short" (either due to a short + * snapshot length or due to lack of reassembly of + * fragments/segments/whatever), we want to display + * what's available in the field (probably FT_BYTES + * or FT_STRING) and then throw an exception later + * + * or + * + * 3) the field is defined to be "what's left in the + * packet" + * + * so we set the length to what remains in the tvbuff so + * that, if we throw an exception while dissecting, it + * has what is probably the right value. + * + * For FT_STRINGZ, it means "the string is null-terminated, + * not null-padded; set the length to the actual length + * of the string", and if the tvbuff if short, we just + * throw an exception. + * + * It's not valid for any other type of field. + */ + switch (hfinfo->type) { + + case FT_PROTOCOL: + /* + * We allow this to be zero-length - for + * example, an ONC RPC NULL procedure has + * neither arguments nor reply, so the + * payload for that protocol is empty. + * + * However, if the length is negative, the + * start offset is *past* the byte past the + * end of the tvbuff, so we throw an + * exception. + */ + *length = tvb_length_remaining(tvb, start); + if (*length < 0) { + /* + * Use "tvb_ensure_bytes_exist()" + * to force the appropriate exception + * to be thrown. + */ + tvb_ensure_bytes_exist(tvb, start, 0); + } + DISSECTOR_ASSERT(*length >= 0); + break; + + case FT_NONE: + case FT_BYTES: + case FT_STRING: + *length = tvb_ensure_length_remaining(tvb, start); + DISSECTOR_ASSERT(*length >= 0); + break; + + case FT_STRINGZ: + /* + * Leave the length as -1, so our caller knows + * it was -1. + */ + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + *item_length = *length; + } else { + *item_length = *length; + if (hfinfo->type == FT_PROTOCOL || hfinfo->type == FT_NONE) { + /* + * These types are for interior nodes of the + * tree, and don't have data associated with + * them; if the length is negative (XXX - see + * above) or goes past the end of the tvbuff, + * cut it short at the end of the tvbuff. + * That way, if this field is selected in + * Wireshark, we don't highlight stuff past + * the end of the data. + */ + /* XXX - what to do, if we don't have a tvb? */ + if (tvb) { + length_remaining = tvb_length_remaining(tvb, start); + if (*item_length < 0 || + (*item_length > 0 && + (length_remaining < *item_length))) + *item_length = length_remaining; + } + } + if (*item_length < 0) { + THROW(ReportedBoundsError); + } + } +} + +static field_info * +new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, const gint item_length) +{ + field_info *fi; + + FIELD_INFO_NEW(fi); + + fi->hfinfo = hfinfo; + fi->start = start; + fi->start += (tvb)?tvb_raw_offset(tvb):0; + fi->length = item_length; + fi->tree_type = -1; + fi->flags = 0; + if (!PTREE_DATA(tree)->visible) + FI_SET_FLAG(fi, FI_HIDDEN); + fvalue_init(&fi->value, fi->hfinfo->type); + fi->rep = NULL; + + /* add the data source tvbuff */ + fi->ds_tvb = tvb ? tvb_get_ds_tvb(tvb) : NULL; + + fi->appendix_start = 0; + fi->appendix_length = 0; + + return fi; +} + +static field_info * +alloc_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, const gint start, + gint *length) +{ + gint item_length; + + get_hfi_and_length(hfinfo, tvb, start, length, &item_length); + return new_field_info(tree, hfinfo, tvb, start, item_length); +} + +/* If the protocol tree is to be visible, set the representation of a + proto_tree entry with the name of the field for the item and with + the value formatted with the supplied printf-style format and + argument list. */ +static void +proto_tree_set_representation_value(proto_item *pi, const char *format, va_list ap) +{ + g_assert(pi); + + /* If the tree (GUI) or item isn't visible it's pointless for us to generate the protocol + * items string representation */ + if (PTREE_DATA(pi)->visible && !PROTO_ITEM_IS_HIDDEN(pi)) { + int ret = 0; + field_info *fi = PITEM_FINFO(pi); + header_field_info *hf; + + DISSECTOR_ASSERT(fi); + + hf = fi->hfinfo; + + ITEM_LABEL_NEW(fi->rep); + if (hf->bitmask && (hf->type == FT_BOOLEAN || IS_FT_UINT(hf->type))) { + guint32 val; + char *p; + + val = fvalue_get_uinteger(&fi->value); + if (hf->bitshift > 0) + val <<= hf->bitshift; + + p = decode_bitfield_value(fi->rep->representation, val, hf->bitmask, hfinfo_bitwidth(hf)); + ret = (int) (p - fi->rep->representation); + } + + /* put in the hf name */ + ret += g_snprintf(fi->rep->representation + ret, ITEM_LABEL_LENGTH - ret, "%s: ", hf->name); + + /* If possible, Put in the value of the string */ + if (ret < ITEM_LABEL_LENGTH) { + ret += g_vsnprintf(fi->rep->representation + ret, + ITEM_LABEL_LENGTH - ret, format, ap); + } + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user + * that the field is truncated. + */ + char *oldrep; + + /* Argh, we cannot reuse 'ap' here. So make a copy + * of what we formatted for (re)use below. + */ + oldrep = g_strdup(fi->rep->representation); + + g_snprintf(fi->rep->representation, + ITEM_LABEL_LENGTH, + "[truncated] %s", + oldrep); + g_free(oldrep); + } + } +} + +/* If the protocol tree is to be visible, set the representation of a + proto_tree entry with the representation formatted with the supplied + printf-style format and argument list. */ +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap) +{ + int ret; /*tmp return value */ + field_info *fi = PITEM_FINFO(pi); + + DISSECTOR_ASSERT(fi); + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + ITEM_LABEL_NEW(fi->rep); + ret = g_vsnprintf(fi->rep->representation, ITEM_LABEL_LENGTH, + format, ap); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user + * that the field is truncated. + */ + char *oldrep; + + /* Argh, we cannot reuse 'ap' here. So make a copy + * of what we formatted for (re)use below. + */ + oldrep = g_strdup(fi->rep->representation); + + g_snprintf(fi->rep->representation, ITEM_LABEL_LENGTH, + "[truncated] %s", oldrep); + g_free(oldrep); + } + } +} + +static int +protoo_strlcpy(gchar *dest, const gchar *src, gsize dest_size) +{ + gsize res = g_strlcpy(dest, src, dest_size); + + if (res > dest_size) + res = dest_size; + return (int) res; +} + +/* -------------------------- */ +const gchar * +proto_custom_set(proto_tree* tree, const int field_id, gint occurrence, + gchar *result, gchar *expr, const int size) +{ + guint32 u_integer; + gint32 integer; + guint8 *bytes; + ipv4_addr *ipv4; + struct e_in6_addr *ipv6; + address addr; + guint32 n_addr; /* network-order IPv4 address */ + + const true_false_string *tfstring; + + int len, prev_len = 0, last, i, offset_r = 0, offset_e = 0; + GPtrArray *finfos; + field_info *finfo = NULL; + header_field_info* hfinfo; + const gchar *abbrev = NULL; + + char number_buf[32]; + const char *number_out; + + g_assert(field_id >= 0); + + hfinfo = proto_registrar_get_nth((guint)field_id); + + /* do we need to rewind ? */ + if (!hfinfo) + return ""; + + if (occurrence < 0) { + /* Search other direction */ + while (hfinfo->same_name_prev) { + hfinfo = hfinfo->same_name_prev; + } + } + + while (hfinfo) { + finfos = proto_get_finfo_ptr_array(tree, hfinfo->id); + + if (!finfos || !(len = g_ptr_array_len(finfos))) { + if (occurrence < 0) { + hfinfo = hfinfo->same_name_next; + } else { + hfinfo = hfinfo->same_name_prev; + } + continue; + } + + /* Are there enough occurrences of the field? */ + if (((occurrence - prev_len) > len) || ((occurrence + prev_len) < -len)) { + if (occurrence < 0) { + hfinfo = hfinfo->same_name_next; + } else { + hfinfo = hfinfo->same_name_prev; + } + prev_len += len; + continue; + } + + /* Calculate single index or set outer bounderies */ + if (occurrence < 0) { + i = occurrence + len + prev_len; + last = i; + } else if (occurrence > 0) { + i = occurrence - 1 - prev_len; + last = i; + } else { + i = 0; + last = len - 1; + } + + prev_len += len; /* Count handled occurrences */ + + while (i <= last) { + finfo = (field_info *)g_ptr_array_index(finfos, i); + + if (offset_r && (offset_r < (size - 2))) + result[offset_r++] = ','; + + if (offset_e && (offset_e < (size - 2))) + expr[offset_e++] = ','; + + switch (hfinfo->type) { + + case FT_NONE: /* Nothing to add */ + if (offset_r == 0) { + result[0] = '\0'; + } else if (result[offset_r-1] == ',') { + result[offset_r-1] = '\0'; + } + break; + + case FT_PROTOCOL: + /* prevent multiple "yes" entries by setting result directly */ + g_strlcpy(result, "Yes", size); + break; + + case FT_UINT_BYTES: + case FT_BYTES: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + bytes_to_str(bytes, + fvalue_length(&finfo->value)), + size-offset_r); + break; + + case FT_ABSOLUTE_TIME: + offset_r += protoo_strlcpy(result+offset_r, + abs_time_to_str((const nstime_t *)fvalue_get(&finfo->value), + (absolute_time_display_e)hfinfo->display, TRUE), + size-offset_r); + break; + + case FT_RELATIVE_TIME: + offset_r += protoo_strlcpy(result+offset_r, + rel_time_to_secs_str((const nstime_t *)fvalue_get(&finfo->value)), + size-offset_r); + break; + + case FT_BOOLEAN: + u_integer = fvalue_get_uinteger(&finfo->value); + tfstring = (const true_false_string *)&tfs_true_false; + if (hfinfo->strings) { + tfstring = (const struct true_false_string*) hfinfo->strings; + } + offset_r += protoo_strlcpy(result+offset_r, + u_integer ? + tfstring->true_string : + tfstring->false_string, size-offset_r); + + offset_e += protoo_strlcpy(expr+offset_e, + u_integer ? "1" : "0", size-offset_e); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + u_integer = fvalue_get_uinteger(&finfo->value); + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, u_integer); + g_strlcpy(result+offset_r, tmp, size-offset_r); + } else if (hfinfo->strings) { + const char *str_val = hf_try_val_to_str(u_integer, hfinfo); + + if (str_val) + g_strlcpy(result+offset_r, str_val, size-offset_r); + else + g_snprintf(result+offset_r, size-offset_r, "%u", u_integer); + + } else { + number_out = hfinfo_number_value_format(hfinfo, number_buf, u_integer); + + g_strlcpy(result+offset_r, number_out, size-offset_r); + } + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + g_snprintf(expr+offset_e, size-offset_e, + "\"%s\"", result+offset_r); + } else { + number_out = hfinfo_numeric_value_format(hfinfo, number_buf, u_integer); + + g_strlcpy(expr+offset_e, number_out, size-offset_e); + } + + offset_r = (int)strlen(result); + offset_e = (int)strlen(expr); + break; + + case FT_INT64: + /* XXX: Should handle BASE_CUSTOM ? */ + g_snprintf(result+offset_r, size-offset_r, + "%" G_GINT64_MODIFIER "d", + fvalue_get_integer64(&finfo->value)); + offset_r = (int)strlen(result); + break; + case FT_UINT64: + g_snprintf(result+offset_r, size-offset_r, + /* XXX: Should handle BASE_CUSTOM ? */ + "%" G_GINT64_MODIFIER "u", + fvalue_get_integer64(&finfo->value)); + offset_r = (int)strlen(result); + break; + case FT_EUI64: + offset_r += protoo_strlcpy(result+offset_r, + eui64_to_str(fvalue_get_integer64(&finfo->value)), + size-offset_r); + break; + /* XXX - make these just FT_INT? */ + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + integer = fvalue_get_sinteger(&finfo->value); + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, integer); + g_strlcpy(result+offset_r, tmp, size-offset_r); + } else if (hfinfo->strings) { + const char *str_val = hf_try_val_to_str(integer, hfinfo); + + if (str_val) + g_strlcpy(result+offset_r, str_val, size-offset_r); + else + g_snprintf(result+offset_r, size-offset_r, "%d", integer); + + } else { + number_out = hfinfo_number_value_format(hfinfo, number_buf, integer); + + g_strlcpy(result+offset_r, number_out, size-offset_r); + } + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + g_snprintf(expr+offset_e, size-offset_e, "\"%s\"", result+offset_r); + } else { + number_out = hfinfo_numeric_value_format(hfinfo, number_buf, (guint32) integer); + + g_strlcpy(expr+offset_e, number_out, size-offset_e); + } + + offset_r = (int)strlen(result); + offset_e = (int)strlen(expr); + break; + + case FT_IPv4: + ipv4 = (ipv4_addr *)fvalue_get(&finfo->value); + n_addr = ipv4_get_net_order_addr(ipv4); + offset_r += protoo_strlcpy(result+offset_r, + ip_to_str((guint8 *)&n_addr), + size-offset_r); + break; + + case FT_IPv6: + ipv6 = (struct e_in6_addr *)fvalue_get(&finfo->value); + SET_ADDRESS (&addr, AT_IPv6, sizeof(struct e_in6_addr), ipv6); + address_to_str_buf(&addr, result+offset_r, size-offset_r); + offset_r = (int)strlen(result); + break; + + case FT_ETHER: + offset_r += protoo_strlcpy(result+offset_r, + bytes_to_str_punct((const guint8 *)fvalue_get(&finfo->value), + FT_ETHER_LEN, ':'), + size-offset_r); + break; + + case FT_GUID: + offset_r += protoo_strlcpy(result+offset_r, + guid_to_str((e_guid_t *)fvalue_get(&finfo->value)), + size-offset_r); + break; + + case FT_OID: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + oid_resolved_from_encoded(bytes, + fvalue_length(&finfo->value)), + size-offset_r); + offset_e += protoo_strlcpy(expr+offset_e, + oid_encoded2string(bytes, fvalue_length(&finfo->value)), + size-offset_e); + break; + + case FT_FLOAT: + g_snprintf(result+offset_r, size-offset_r, + "%." STRINGIFY(FLT_DIG) "g", fvalue_get_floating(&finfo->value)); + offset_r = (int)strlen(result); + break; + + case FT_DOUBLE: + g_snprintf(result+offset_r, size-offset_r, + "%." STRINGIFY(DBL_DIG) "g", fvalue_get_floating(&finfo->value)); + offset_r = (int)strlen(result); + break; + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + format_text(bytes, strlen(bytes)), + size-offset_r); + break; + + case FT_IPXNET: /*XXX really No column custom ?*/ + case FT_PCRE: + default: + g_error("hfinfo->type %d (%s) not handled\n", + hfinfo->type, + ftype_name(hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + i++; + } + + switch (hfinfo->type) { + + case FT_BOOLEAN: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_OID: + /* for these types, "expr" is filled in the loop above */ + break; + + default: + /* for all others, just copy "result" to "expr" */ + g_strlcpy(expr, result, size); + break; + } + + if (!abbrev) { + /* Store abbrev for return value */ + abbrev = hfinfo->abbrev; + } + + if (occurrence == 0) { + /* Fetch next hfinfo with same name (abbrev) */ + hfinfo = hfinfo->same_name_prev; + } else { + hfinfo = NULL; + } + } + + return abbrev ? abbrev : ""; +} + + +/* Set text of proto_item after having already been created. */ +void +proto_item_set_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + if (fi->rep) { + ITEM_LABEL_FREE(fi->rep); + fi->rep = NULL; + } + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); +} + +/* Append to text of proto_item after having already been created. */ +void +proto_item_append_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + size_t curlen; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) { + return; + } + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + /* + * If we don't already have a representation, + * generate the default representation. + */ + if (fi->rep == NULL) { + ITEM_LABEL_NEW(fi->rep); + proto_item_fill_label(fi, fi->rep->representation); + } + + curlen = strlen(fi->rep->representation); + if (ITEM_LABEL_LENGTH > curlen) { + va_start(ap, format); + g_vsnprintf(fi->rep->representation + curlen, + ITEM_LABEL_LENGTH - (gulong) curlen, format, ap); + va_end(ap); + } + } +} + +/* Prepend to text of proto_item after having already been created. */ +void +proto_item_prepend_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + char representation[ITEM_LABEL_LENGTH]; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) { + return; + } + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + /* + * If we don't already have a representation, + * generate the default representation. + */ + if (fi->rep == NULL) { + ITEM_LABEL_NEW(fi->rep); + proto_item_fill_label(fi, representation); + } else + g_strlcpy(representation, fi->rep->representation, ITEM_LABEL_LENGTH); + + va_start(ap, format); + g_vsnprintf(fi->rep->representation, + ITEM_LABEL_LENGTH, format, ap); + va_end(ap); + g_strlcat(fi->rep->representation, representation, ITEM_LABEL_LENGTH); + } +} + +void +proto_item_set_len(proto_item *pi, const gint length) +{ + field_info *fi; + + if (pi == NULL) + return; + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + DISSECTOR_ASSERT(length >= 0); + fi->length = length; + + /* + * You cannot just make the "len" field of a GByteArray + * larger, if there's no data to back that length; + * you can only make it smaller. + */ + if (fi->value.ftype->ftype == FT_BYTES && length <= (gint)fi->value.value.bytes->len) + fi->value.value.bytes->len = length; +} + +/* + * Sets the length of the item based on its start and on the specified + * offset, which is the offset past the end of the item; as the start + * in the item is relative to the beginning of the data source tvbuff, + * we need to pass in a tvbuff - the end offset is relative to the beginning + * of that tvbuff. + */ +void +proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end) +{ + field_info *fi; + + if (pi == NULL) + return; + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + end += tvb_raw_offset(tvb); + DISSECTOR_ASSERT(end >= fi->start); + fi->length = end - fi->start; +} + +int +proto_item_get_len(const proto_item *pi) +{ + field_info *fi = PITEM_FINFO(pi); + return fi ? fi->length : -1; +} + + +/** clear flags according to the mask and set new flag values */ +#define FI_REPLACE_FLAGS(fi, mask, flags_in) { \ + (fi->flags = (fi)->flags & ~(mask)); \ + (fi->flags = (fi)->flags | (flags_in)); \ +} + +gboolean +proto_item_set_expert_flags(proto_item *pi, const int group, const guint severity) +{ + if (pi == NULL || PITEM_FINFO(pi) == NULL) + return FALSE; + + /* only change things if severity is worse or at least equal than before */ + if (severity >= FI_GET_FLAG(PITEM_FINFO(pi), PI_SEVERITY_MASK)) { + FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_GROUP_MASK, group); + FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_SEVERITY_MASK, severity); + + return TRUE; + } + + return FALSE; +} + +proto_tree * +proto_tree_create_root(packet_info *pinfo) +{ + proto_node *pnode; + + /* Initialize the proto_node */ + PROTO_NODE_NEW(pnode); + pnode->parent = NULL; + PNODE_FINFO(pnode) = NULL; + pnode->tree_data = g_new(tree_data_t, 1); + + /* Make sure we can access pinfo everywhere */ + pnode->tree_data->pinfo = pinfo; + + /* Don't initialize the tree_data_t. Wait until we know we need it */ + pnode->tree_data->interesting_hfids = NULL; + + /* Set the default to FALSE so it's easier to + * find errors; if we expect to see the protocol tree + * but for some reason the default 'visible' is not + * changed, then we'll find out very quickly. */ + pnode->tree_data->visible = FALSE; + + /* Make sure that we fake protocols (if possible) */ + pnode->tree_data->fake_protocols = TRUE; + + /* Keep track of the number of children */ + pnode->tree_data->count = 0; + + pnode->tree_data->fi_tmp = NULL; + + return (proto_tree *)pnode; +} + + +/* "prime" a proto_tree with a single hfid that a dfilter + * is interested in. */ +void +proto_tree_prime_hfid(proto_tree *tree _U_, const gint hfid) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfid, hfinfo); + /* this field is referenced by a filter so increase the refcount. + also increase the refcount for the parent, i.e the protocol. + */ + hfinfo->ref_type = HF_REF_TYPE_DIRECT; + /* only increase the refcount if there is a parent. + if this is a protocol and not a field then parent will be -1 + and there is no parent to add any refcounting for. + */ + if (hfinfo->parent != -1) { + header_field_info *parent_hfinfo; + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + + /* Mark parent as indirectly referenced unless it is already directly + * referenced, i.e. the user has specified the parent in a filter. + */ + if (parent_hfinfo->ref_type != HF_REF_TYPE_DIRECT) + parent_hfinfo->ref_type = HF_REF_TYPE_INDIRECT; + } +} + +proto_tree * +proto_item_add_subtree(proto_item *pi, const gint idx) { + field_info *fi; + + if (!pi) + return NULL; + + DISSECTOR_ASSERT(idx >= 0 && idx < num_tree_types); + + fi = PITEM_FINFO(pi); + if (!fi) + return (proto_tree *)pi; + + fi->tree_type = idx; + + return (proto_tree *)pi; +} + +proto_tree * +proto_item_get_subtree(const proto_item *pi) { + field_info *fi; + + if (!pi) + return NULL; + fi = PITEM_FINFO(pi); + if ( (!fi) || (fi->tree_type == -1) ) + return NULL; + return (proto_tree *)pi; +} + +proto_item * +proto_item_get_parent(const proto_item *ti) { + if (!ti) + return NULL; + return ti->parent; +} + +proto_item * +proto_item_get_parent_nth(proto_item *ti, int gen) { + if (!ti) + return NULL; + while (gen--) { + ti = ti->parent; + if (!ti) + return NULL; + } + return ti; +} + + +proto_item * +proto_tree_get_parent(const proto_tree *tree) { + if (!tree) + return NULL; + return (proto_item *)tree; +} + +proto_tree * +proto_tree_get_root(proto_tree *tree) { + if (!tree) + return NULL; + while (tree->parent) { + tree = tree->parent; + } + return tree; +} + +void +proto_tree_move_item(proto_tree *tree, proto_item *fixed_item, + proto_item *item_to_move) +{ + + /* Revert part of: http://anonsvn.wireshark.org/viewvc?view=rev&revision=32443 + * See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5500 + */ + /* This function doesn't generate any values. It only reorganizes the prococol tree + * so we can bail out immediately if it isn't visible. */ + if (!tree || !PTREE_DATA(tree)->visible) + return; + + DISSECTOR_ASSERT(item_to_move->parent == tree); + DISSECTOR_ASSERT(fixed_item->parent == tree); + + /*** cut item_to_move out ***/ + + /* is item_to_move the first? */ + if (tree->first_child == item_to_move) { + /* simply change first child to next */ + tree->first_child = item_to_move->next; + + DISSECTOR_ASSERT(tree->last_child != item_to_move); + } else { + proto_item *curr_item; + /* find previous and change it's next */ + for(curr_item = tree->first_child; curr_item != NULL; curr_item = curr_item->next) { + if (curr_item->next == item_to_move) { + break; + } + } + + DISSECTOR_ASSERT(curr_item); + + curr_item->next = item_to_move->next; + + /* fix last_child if required */ + if (tree->last_child == item_to_move) { + tree->last_child = curr_item; + } + } + + /*** insert to_move after fixed ***/ + item_to_move->next = fixed_item->next; + fixed_item->next = item_to_move; + if (tree->last_child == fixed_item) { + tree->last_child = item_to_move; + } +} + +void +proto_tree_set_appendix(proto_tree *tree, tvbuff_t *tvb, gint start, + const gint length) +{ + field_info *fi; + + if (tree == NULL) + return; + + fi = PTREE_FINFO(tree); + if (fi == NULL) + return; + + start += tvb_raw_offset(tvb); + DISSECTOR_ASSERT(start >= 0); + DISSECTOR_ASSERT(length >= 0); + + fi->appendix_start = start; + fi->appendix_length = length; +} + +int +proto_register_protocol(const char *name, const char *short_name, + const char *filter_name) +{ + protocol_t *protocol; + header_field_info *hfinfo; + int proto_id; + char *existing_name; + gint *key; + guint i; + guchar c; + gboolean found_invalid; + + /* + * Make sure there's not already a protocol with any of those + * names. Crash if there is, as that's an error in the code + * or an inappropriate plugin. + * This situation has to be fixed to not register more than one + * protocol with the same name. + * + * This is done by reducing the number of strcmp (and alike) calls + * as much as possible, as this significally slows down startup time. + * + * Drawback: As a hash value is used to reduce insert time, + * this might lead to a hash collision. + * However, although we have somewhat over 1000 protocols, we're using + * a 32 bit int so this is very, very unlikely. + */ + + key = (gint *)g_malloc (sizeof(gint)); + *key = wrs_str_hash(name); + + existing_name = (char *)g_hash_table_lookup(proto_names, key); + if (existing_name != NULL) { + /* g_error will terminate the program */ + g_error("Duplicate protocol name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", name); + } + g_hash_table_insert(proto_names, key, (gpointer)name); + + existing_name = (char *)g_hash_table_lookup(proto_short_names, (gpointer)short_name); + if (existing_name != NULL) { + g_error("Duplicate protocol short_name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", short_name); + } + g_hash_table_insert(proto_short_names, (gpointer)short_name, (gpointer)short_name); + + found_invalid = FALSE; + for (i = 0; filter_name[i]; i++) { + c = filter_name[i]; + if (!(islower(c) || isdigit(c) || c == '-' || c == '_' || c == '.')) { + found_invalid = TRUE; + } + } + if (found_invalid) { + g_error("Protocol filter name \"%s\" has one or more invalid characters." + " Allowed are lower characters, digits, '-', '_' and '.'." + " This might be caused by an inappropriate plugin or a development error.", filter_name); + } + existing_name = (char *)g_hash_table_lookup(proto_filter_names, (gpointer)filter_name); + if (existing_name != NULL) { + g_error("Duplicate protocol filter_name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", filter_name); + } + g_hash_table_insert(proto_filter_names, (gpointer)filter_name, (gpointer)filter_name); + + /* Add this protocol to the list of known protocols; the list + is sorted by protocol short name. */ + protocol = g_new(protocol_t, 1); + protocol->name = name; + protocol->short_name = short_name; + protocol->filter_name = filter_name; + protocol->fields = NULL; + protocol->is_enabled = TRUE; /* protocol is enabled by default */ + protocol->can_toggle = TRUE; + protocol->is_private = FALSE; + /* list will be sorted later by name, when all protocols completed registering */ + protocols = g_list_prepend(protocols, protocol); + + /* Here we allocate a new header_field_info struct */ + hfinfo = g_slice_new(header_field_info); + hfinfo->name = name; + hfinfo->abbrev = filter_name; + hfinfo->type = FT_PROTOCOL; + hfinfo->display = BASE_NONE; + hfinfo->strings = protocol; + hfinfo->bitmask = 0; + hfinfo->bitshift = 0; + hfinfo->ref_type = HF_REF_TYPE_NONE; + hfinfo->blurb = NULL; + hfinfo->parent = -1; /* this field differentiates protos and fields */ + + proto_id = proto_register_field_init(hfinfo, hfinfo->parent); + protocol->proto_id = proto_id; + return proto_id; +} + +void +proto_mark_private(const int proto_id) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + if (protocol) + protocol->is_private = TRUE; +} + +gboolean +proto_is_private(const int proto_id) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + if (protocol) + return protocol->is_private; + else + return FALSE; +} + +/* + * Routines to use to iterate over the protocols. + * The argument passed to the iterator routines is an opaque cookie to + * their callers; it's the GList pointer for the current element in + * the list. + * The ID of the protocol is returned, or -1 if there is no protocol. + */ +int +proto_get_first_protocol(void **cookie) +{ + protocol_t *protocol; + + if (protocols == NULL) + return -1; + *cookie = protocols; + protocol = (protocol_t *)protocols->data; + return protocol->proto_id; +} + +int +proto_get_data_protocol(void *cookie) +{ + GList *list_item = (GList *)cookie; + + protocol_t *protocol = (protocol_t *)list_item->data; + return protocol->proto_id; +} + +int +proto_get_next_protocol(void **cookie) +{ + GList *list_item = (GList *)*cookie; + protocol_t *protocol; + + list_item = g_list_next(list_item); + if (list_item == NULL) + return -1; + *cookie = list_item; + protocol = (protocol_t *)list_item->data; + return protocol->proto_id; +} + +header_field_info * +proto_get_first_protocol_field(const int proto_id, void **cookie) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + hf_register_info *ptr; + + if ((protocol == NULL) || (protocol->fields == NULL)) + return NULL; + + *cookie = protocol->fields; + ptr = (hf_register_info *)protocol->fields->data; + return &ptr->hfinfo; +} + +header_field_info * +proto_get_next_protocol_field(void **cookie) +{ + GList *list_item = (GList *)*cookie; + hf_register_info *ptr; + + list_item = g_list_next(list_item); + if (list_item == NULL) + return NULL; + + *cookie = list_item; + ptr = (hf_register_info *)list_item->data; + return &ptr->hfinfo; +} + +protocol_t * +find_protocol_by_id(const int proto_id) +{ + header_field_info *hfinfo; + + if (proto_id < 0) + return NULL; + + PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo); + DISSECTOR_ASSERT(hfinfo->type == FT_PROTOCOL); + return (protocol_t *)hfinfo->strings; +} + +static gint compare_filter_name(gconstpointer proto_arg, + gconstpointer filter_name) +{ + const protocol_t *protocol = (const protocol_t *)proto_arg; + const gchar *f_name = (const gchar *)filter_name; + + return (strcmp(protocol->filter_name, f_name)); +} + +int +proto_get_id(const protocol_t *protocol) +{ + return protocol->proto_id; +} + +int proto_get_id_by_filter_name(const gchar* filter_name) +{ + GList *list_entry; + protocol_t *protocol; + + if(!filter_name){ + fprintf(stderr, "No filter name present"); + DISSECTOR_ASSERT(filter_name); + } + + list_entry = g_list_find_custom(protocols, filter_name, + compare_filter_name); + + if (list_entry == NULL) + return -1; + protocol = (protocol_t *)list_entry->data; + return protocol->proto_id; +} + +const char * +proto_get_protocol_name(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + + if (protocol == NULL) + return NULL; + return protocol->name; +} + +const char * +proto_get_protocol_short_name(const protocol_t *protocol) +{ + if (protocol == NULL) + return "(none)"; + return protocol->short_name; +} + +const char * +proto_get_protocol_long_name(const protocol_t *protocol) +{ + if (protocol == NULL) + return "(none)"; + return protocol->name; +} + +const char * +proto_get_protocol_filter_name(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + if (protocol == NULL) + return "(none)"; + return protocol->filter_name; +} + +gboolean +proto_is_protocol_enabled(const protocol_t *protocol) +{ + return protocol->is_enabled; +} + +gboolean +proto_can_toggle_protocol(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + return protocol->can_toggle; +} + +void +proto_set_decoding(const int proto_id, const gboolean enabled) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + DISSECTOR_ASSERT(protocol->can_toggle); + protocol->is_enabled = enabled; +} + +void +proto_enable_all(void) +{ + protocol_t *protocol; + GList *list_item = protocols; + + if (protocols == NULL) + return; + + while (list_item) { + protocol = (protocol_t *)list_item->data; + if (protocol->can_toggle) + protocol->is_enabled = TRUE; + list_item = g_list_next(list_item); + } +} + +void +proto_set_cant_toggle(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + protocol->can_toggle = FALSE; +} + +/* for use with static arrays only, since we don't allocate our own copies +of the header_field_info struct contained within the hf_register_info struct */ +void +proto_register_field_array(const int parent, hf_register_info *hf, const int num_records) +{ + int field_id, i; + hf_register_info *ptr = hf; + protocol_t *proto; + + proto = find_protocol_by_id(parent); + for (i = 0; i < num_records; i++, ptr++) { + /* + * Make sure we haven't registered this yet. + * Most fields have variables associated with them + * that are initialized to -1; some have array elements, + * or possibly uninitialized variables, so we also allow + * 0 (which is unlikely to be the field ID we get back + * from "proto_register_field_init()"). + */ + if (*ptr->p_id != -1 && *ptr->p_id != 0) { + fprintf(stderr, + "Duplicate field detected in call to proto_register_field_array: %s is already registered\n", + ptr->hfinfo.abbrev); + return; + } + + if (proto != NULL) { + if (proto->fields == NULL) { + proto->fields = g_list_append(NULL, ptr); + proto->last_field = proto->fields; + } else { + proto->last_field = + g_list_append(proto->last_field, ptr)->next; + } + } + field_id = proto_register_field_init(&ptr->hfinfo, parent); + *ptr->p_id = field_id; + } +} + +/* unregister already registered fields */ +void +proto_unregister_field (const int parent, gint hf_id) +{ + hf_register_info *hf; + protocol_t *proto; + GList *field; + + if (hf_id == -1 || hf_id == 0) + return; + + proto = find_protocol_by_id (parent); + if (!proto || !proto->fields) { + return; + } + + for (field = g_list_first (proto->fields); field; field = g_list_next (field)) { + hf = (hf_register_info *)field->data; + if (*hf->p_id == hf_id) { + /* Found the hf_id in this protocol */ + g_tree_steal (gpa_name_tree, hf->hfinfo.abbrev); + proto->fields = g_list_remove_link (proto->fields, field); + proto->last_field = g_list_last (proto->fields); + break; + } + } +} + +/* chars allowed in field abbrev */ +static +const guchar fld_abbrev_chars[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, /* 0x20-0x2F '-', '.' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30-0x3F '0'-'9' */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40-0x4F 'A'-'O' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50-0x5F 'P'-'Z', '_' */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60-0x6F 'a'-'o' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70-0x7F 'p'-'z' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0-0xAF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0-0xBF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0-0xCF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0-0xDF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0-0xEF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0-0xFF */ +}; + +static const value_string hf_types[] = { + { FT_NONE, "FT_NONE" }, + { FT_PROTOCOL, "FT_PROTOCOL" }, + { FT_BOOLEAN, "FT_BOOLEAN" }, + { FT_UINT8, "FT_UINT8" }, + { FT_UINT16, "FT_UINT16" }, + { FT_UINT24, "FT_UINT24" }, + { FT_UINT32, "FT_UINT32" }, + { FT_UINT64, "FT_UINT64" }, + { FT_INT8, "FT_INT8" }, + { FT_INT16, "FT_INT16" }, + { FT_INT24, "FT_INT24" }, + { FT_INT32, "FT_INT32" }, + { FT_INT64, "FT_INT64" }, + { FT_EUI64, "FT_EUI64" }, + { FT_FLOAT, "FT_FLOAT" }, + { FT_DOUBLE, "FT_DOUBLE" }, + { FT_ABSOLUTE_TIME, "FT_ABSOLUTE_TIME" }, + { FT_RELATIVE_TIME, "FT_RELATIVE_TIME" }, + { FT_STRING, "FT_STRING" }, + { FT_STRINGZ, "FT_STRINGZ" }, + { FT_UINT_STRING, "FT_UINT_STRING" }, + { FT_ETHER, "FT_ETHER" }, + { FT_BYTES, "FT_BYTES" }, + { FT_UINT_BYTES, "FT_UINT_BYTES" }, + { FT_IPv4, "FT_IPv4" }, + { FT_IPv6, "FT_IPv6" }, + { FT_IPXNET, "FT_IPXNET" }, + { FT_FRAMENUM, "FT_FRAMENUM" }, + { FT_PCRE, "FT_PCR" }, + { FT_GUID, "FT_GUID" }, + { FT_OID, "FT_OID" }, + { 0, NULL } }; + +static const value_string hf_display[] = { + { BASE_NONE, "BASE_NONE" }, + { BASE_DEC, "BASE_DEC" }, + { BASE_HEX, "BASE_HEX" }, + { BASE_OCT, "BASE_OCT" }, + { BASE_DEC_HEX, "BASE_DEC_HEX" }, + { BASE_HEX_DEC, "BASE_HEX_DEC" }, + { BASE_CUSTOM, "BASE_CUSTOM" }, + { BASE_NONE|BASE_RANGE_STRING, "BASE_NONE|BASE_RANGE_STRING" }, + { BASE_DEC|BASE_RANGE_STRING, "BASE_DEC|BASE_RANGE_STRING" }, + { BASE_HEX|BASE_RANGE_STRING, "BASE_HEX|BASE_RANGE_STRING" }, + { BASE_OCT|BASE_RANGE_STRING, "BASE_OCT|BASE_RANGE_STRING" }, + { BASE_DEC_HEX|BASE_RANGE_STRING, "BASE_DEC_HEX|BASE_RANGE_STRING" }, + { BASE_HEX_DEC|BASE_RANGE_STRING, "BASE_HEX_DEC|BASE_RANGE_STRING" }, + { BASE_CUSTOM|BASE_RANGE_STRING, "BASE_CUSTOM|BASE_RANGE_STRING" }, + { ABSOLUTE_TIME_LOCAL, "ABSOLUTE_TIME_LOCAL" }, + { ABSOLUTE_TIME_UTC, "ABSOLUTE_TIME_UTC" }, + { ABSOLUTE_TIME_DOY_UTC, "ABSOLUTE_TIME_DOY_UTC" }, + { 0, NULL } }; + +/* temporary function containing assert part for easier profiling */ +static void +tmp_fld_check_assert(header_field_info *hfinfo) +{ + + /* The field must have a name (with length > 0) */ + if (!hfinfo->name || !hfinfo->name[0]) { + if (hfinfo->abbrev) + /* Try to identify the field */ + g_error("Field (abbrev='%s') does not have a name\n", + hfinfo->abbrev); + else + /* Hum, no luck */ + g_error("Field does not have a name (nor an abbreviation)\n"); + } + + /* fields with an empty string for an abbreviation aren't filterable */ + if (!hfinfo->abbrev || !hfinfo->abbrev[0]) + g_error("Field '%s' does not have an abbreviation\n", hfinfo->name); + + /* These types of fields are allowed to have value_strings, + * true_false_strings or a protocol_t struct + */ + if (hfinfo->strings != NULL && !( + (hfinfo->type == FT_UINT8) || + (hfinfo->type == FT_UINT16) || + (hfinfo->type == FT_UINT24) || + (hfinfo->type == FT_UINT32) || + (hfinfo->type == FT_UINT64) || + (hfinfo->type == FT_INT8) || + (hfinfo->type == FT_INT16) || + (hfinfo->type == FT_INT24) || + (hfinfo->type == FT_INT32) || + (hfinfo->type == FT_INT64) || + (hfinfo->type == FT_BOOLEAN) || + (hfinfo->type == FT_PROTOCOL) || + (hfinfo->type == FT_FRAMENUM) )) + g_error("Field '%s' (%s) has a 'strings' value but is of type %s" + " (which is not allowed to have strings)\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + + /* TODO: This check may slow down startup, and output quite a few warnings. + It would be good to be able to enable this (and possibly other checks?) + in non-release builds. */ +#if 0 + /* Check for duplicate value_string values. + There are lots that have the same value *and* string, so for now only + report those that have same value but different string. */ + if ((hfinfo->strings != NULL) && + !(hfinfo->display & BASE_RANGE_STRING) && + !((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) && + ( + (hfinfo->type == FT_UINT8) || + (hfinfo->type == FT_UINT16) || + (hfinfo->type == FT_UINT24) || + (hfinfo->type == FT_UINT32) || + (hfinfo->type == FT_INT8) || + (hfinfo->type == FT_INT16) || + (hfinfo->type == FT_INT24) || + (hfinfo->type == FT_INT32) || + (hfinfo->type == FT_FRAMENUM) )) { + + int n, m; + const value_string *start_values; + const value_string *current; + + if (hfinfo->display & BASE_EXT_STRING) + start_values = VALUE_STRING_EXT_VS_P(((const value_string_ext*)hfinfo->strings)); + else + start_values = (const value_string*)hfinfo->strings; + current = start_values; + + for (n=0; current; n++, current++) { + /* Drop out if we reached the end. */ + if ((current->value == 0) && (current->strptr == NULL)) { + break; + } + + /* Check value against all previous */ + for (m=0; m < n; m++) { + /* There are lots of duplicates with the same string, + so only report if different... */ + if ((start_values[m].value == current->value) && + (strcmp(start_values[m].strptr, current->strptr) != 0)) { + g_warning("Field '%s' (%s) has a conflicting entry in its" + " value_string: %u is at indices %u (%s) and %u (%s))\n", + hfinfo->name, hfinfo->abbrev, + current->value, m, start_values[m].strptr, n, current->strptr); + } + } + } + } +#endif + + + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + /* Hexadecimal and octal are, in printf() and everywhere + * else, unsigned so don't allow dissectors to register a + * signed field to be displayed unsigned. (Else how would + * we display negative values?) + */ + if (hfinfo->display == BASE_HEX || hfinfo->display == BASE_HEX_DEC || + hfinfo->display == BASE_DEC_HEX || hfinfo->display == BASE_OCT) + g_error("Field '%s' (%s) is signed (%s) but is being displayed unsigned (%s)\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + /* FALL THROUGH */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + /* Require integral types (other than frame number, + * which is always displayed in decimal) to have a + * number base. + * If there is a strings value then this base is not + * normally used except when constructing a display + * filter for a value not found in the strings lookup. + */ + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_DEC: + case BASE_HEX: + case BASE_OCT: + case BASE_DEC_HEX: + case BASE_HEX_DEC: + case BASE_CUSTOM: /* hfinfo_numeric_value_format() treats this as decimal */ + break; + default: + g_error("Field '%s' (%s) is an integral value (%s)" + " but is being displayed as %s\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Unknown: 0x%x)")); + } + break; + + case FT_PROTOCOL: + case FT_FRAMENUM: + if (hfinfo->display != BASE_NONE) + g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + + case FT_BOOLEAN: + break; + + case FT_ABSOLUTE_TIME: + if (!(hfinfo->display == ABSOLUTE_TIME_LOCAL || + hfinfo->display == ABSOLUTE_TIME_UTC || + hfinfo->display == ABSOLUTE_TIME_DOY_UTC)) + g_error("Field '%s' (%s) is a %s but is being displayed as %s instead of as a time\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + + default: + if (hfinfo->display != BASE_NONE) + g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + if (hfinfo->strings != NULL) + g_error("Field '%s' (%s) is an %s but has a strings value\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + } +} + +#define PROTO_PRE_ALLOC_HF_FIELDS_MEM (120000+PRE_ALLOC_EXPERT_FIELDS_MEM) +static int +proto_register_field_init(header_field_info *hfinfo, const int parent) +{ + + tmp_fld_check_assert(hfinfo); + + /* if this is a bitfield, compute bitshift */ + if (hfinfo->bitmask) { + hfinfo->bitshift = wrs_count_bitshift(hfinfo->bitmask); + } + + hfinfo->parent = parent; + hfinfo->same_name_next = NULL; + hfinfo->same_name_prev = NULL; + + /* if we always add and never delete, then id == len - 1 is correct */ + if (gpa_hfinfo.len >= gpa_hfinfo.allocated_len) { + if (!gpa_hfinfo.hfi) { + gpa_hfinfo.allocated_len = PROTO_PRE_ALLOC_HF_FIELDS_MEM; + gpa_hfinfo.hfi = (header_field_info **)g_malloc(sizeof(header_field_info *)*PROTO_PRE_ALLOC_HF_FIELDS_MEM); + } else { + gpa_hfinfo.allocated_len += 1000; + gpa_hfinfo.hfi = (header_field_info **)g_realloc(gpa_hfinfo.hfi, + sizeof(header_field_info *)*gpa_hfinfo.allocated_len); + /*g_warning("gpa_hfinfo.allocated_len %u", gpa_hfinfo.allocated_len);*/ + } + } + gpa_hfinfo.hfi[gpa_hfinfo.len] = hfinfo; + gpa_hfinfo.len++; + hfinfo->id = gpa_hfinfo.len - 1; + + /* if we have real names, enter this field in the name tree */ + if ((hfinfo->name[0] != 0) && (hfinfo->abbrev[0] != 0 )) { + + header_field_info *same_name_next_hfinfo; + guchar c; + + /* Check that the filter name (abbreviation) is legal; + * it must contain only alphanumerics, '-', "_", and ".". */ + c = wrs_check_charset(fld_abbrev_chars, hfinfo->abbrev); + if (c) { + fprintf(stderr, "Invalid character '%c' in filter name '%s'\n", c, hfinfo->abbrev); + DISSECTOR_ASSERT(!c); + } + + /* We allow multiple hfinfo's to be registered under the same + * abbreviation. This was done for X.25, as, depending + * on whether it's modulo-8 or modulo-128 operation, + * some bitfield fields may be in different bits of + * a byte, and we want to be able to refer to that field + * with one name regardless of whether the packets + * are modulo-8 or modulo-128 packets. */ + + same_name_hfinfo = NULL; + + g_tree_insert(gpa_name_tree, (gpointer) (hfinfo->abbrev), hfinfo); + /* GLIB 2.x - if it is already present + * the previous hfinfo with the same name is saved + * to same_name_hfinfo by value destroy callback */ + if (same_name_hfinfo) { + /* There's already a field with this name. + * Put it after that field in the list of + * fields with this name, then allow the code + * after this if{} block to replace the old + * hfinfo with the new hfinfo in the GTree. Thus, + * we end up with a linked-list of same-named hfinfo's, + * with the root of the list being the hfinfo in the GTree */ + same_name_next_hfinfo = + same_name_hfinfo->same_name_next; + + hfinfo->same_name_next = same_name_next_hfinfo; + if (same_name_next_hfinfo) + same_name_next_hfinfo->same_name_prev = hfinfo; + + same_name_hfinfo->same_name_next = hfinfo; + hfinfo->same_name_prev = same_name_hfinfo; + } + } + + return hfinfo->id; +} + +void +proto_register_subtree_array(gint *const *indices, const int num_indices) +{ + int i; + gint *const *ptr = indices; + + /* + * If we've already allocated the array of tree types, expand + * it; this lets plugins such as mate add tree types after + * the initial startup. (If we haven't already allocated it, + * we don't allocate it; on the first pass, we just assign + * ett values and keep track of how many we've assigned, and + * when we're finished registering all dissectors we allocate + * the array, so that we do only one allocation rather than + * wasting CPU time and memory by growing the array for each + * dissector that registers ett values.) + */ + if (tree_is_expanded != NULL) { + tree_is_expanded = (guint32 *)g_realloc(tree_is_expanded, (1+((num_tree_types + num_indices)/32)) * sizeof(guint32)); + + /* set new items to 0 */ + /* XXX, slow!!! optimize when needed (align 'i' to 32, and set rest of guint32 to 0) */ + for (i = num_tree_types; i < num_tree_types + num_indices; i++) + tree_is_expanded[i >> 5] &= ~(1 << (i & 31)); + } + + /* + * Assign "num_indices" subtree numbers starting at "num_tree_types", + * returning the indices through the pointers in the array whose + * first element is pointed to by "indices", and update + * "num_tree_types" appropriately. + */ + for (i = 0; i < num_indices; i++, ptr++, num_tree_types++) { + if (**ptr != -1) { + /* g_error will terminate the program */ + g_error("register_subtree_array: subtree item type (ett_...) not -1 !" + " This is a development error:" + " Either the subtree item type has already been assigned or" + " was not initialized to -1."); + } + **ptr = num_tree_types; + } +} + +static int +label_fill_descr(char *label_str, const header_field_info *hfinfo, const char *text, const char *descr) +{ + gint ret; + + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s (%s)", hfinfo->name, text, descr); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */ + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s [truncated]: %s (%s)", hfinfo->name, text, descr); + } + + return ret; +} + +static int +label_fill(char *label_str, const header_field_info *hfinfo, const char *text) +{ + gint ret; + + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, text); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */ + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s [truncated]: %s", hfinfo->name, text); + } + + return ret; +} + +void +proto_item_fill_label(field_info *fi, gchar *label_str) +{ + header_field_info *hfinfo; + guint8 *bytes; + guint32 integer; + guint64 integer64; + ipv4_addr *ipv4; + e_guid_t *guid; + guint32 n_addr; /* network-order IPv4 address */ + const gchar *name; + + if (!fi) { + if (label_str) + label_str[0]= '\0'; + /* XXX: Check validity of hfinfo->type */ + return; + } + + hfinfo = fi->hfinfo; + + switch (hfinfo->type) { + case FT_NONE: + case FT_PROTOCOL: + g_strlcpy(label_str, hfinfo->name, ITEM_LABEL_LENGTH); + break; + + case FT_BOOLEAN: + fill_label_boolean(fi, label_str); + break; + + case FT_BYTES: + case FT_UINT_BYTES: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, + (bytes) ? bytes_to_str(bytes, fvalue_length(&fi->value)) : ""); + break; + + /* Four types of integers to take care of: + * Bitfield, with val_string + * Bitfield, w/o val_string + * Non-bitfield, with val_string + * Non-bitfield, w/o val_string + */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + if (hfinfo->bitmask) { + fill_label_bitfield(fi, label_str); + } else { + fill_label_number(fi, label_str, FALSE); + } + break; + + case FT_FRAMENUM: + fill_label_number(fi, label_str, FALSE); + break; + + case FT_UINT64: + fill_label_number64(fi, label_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + DISSECTOR_ASSERT(!hfinfo->bitmask); + fill_label_number(fi, label_str, TRUE); + break; + + case FT_INT64: + fill_label_number64(fi, label_str, TRUE); + break; + + case FT_FLOAT: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %." STRINGIFY(FLT_DIG) "g", + hfinfo->name, fvalue_get_floating(&fi->value)); + break; + + case FT_DOUBLE: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %." STRINGIFY(DBL_DIG) "g", + hfinfo->name, fvalue_get_floating(&fi->value)); + break; + + case FT_ABSOLUTE_TIME: + label_fill(label_str, hfinfo, + abs_time_to_str((const nstime_t *)fvalue_get(&fi->value), + (absolute_time_display_e)hfinfo->display, TRUE)); + break; + + case FT_RELATIVE_TIME: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s seconds", hfinfo->name, + rel_time_to_secs_str((const nstime_t *)fvalue_get(&fi->value))); + break; + + case FT_IPXNET: + integer = fvalue_get_uinteger(&fi->value); + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%08X)", hfinfo->name, + get_ipxnet_name(integer), integer); + break; + + case FT_AX25: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_ax25_name(bytes), + ax25_to_str(bytes)); + break; + + case FT_ETHER: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_ether_name(bytes), + ether_to_str(bytes)); + break; + + case FT_IPv4: + ipv4 = (ipv4_addr *)fvalue_get(&fi->value); + n_addr = ipv4_get_net_order_addr(ipv4); + label_fill_descr(label_str, hfinfo, + get_hostname(n_addr), + ip_to_str((guint8*)&n_addr)); + break; + + case FT_IPv6: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_hostname6((struct e_in6_addr *)bytes), + ip6_to_str((struct e_in6_addr*)bytes)); + break; + + case FT_GUID: + guid = (e_guid_t *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, guid_to_str(guid)); + break; + + case FT_OID: + bytes = (guint8 *)fvalue_get(&fi->value); + name = oid_resolved_from_encoded(bytes, fvalue_length(&fi->value)); + if (name) { + label_fill_descr(label_str, hfinfo, + oid_encoded2string(bytes, fvalue_length(&fi->value)), name); + } else { + label_fill(label_str, hfinfo, + oid_encoded2string(bytes, fvalue_length(&fi->value))); + } + break; + case FT_EUI64: + integer64 = fvalue_get_integer64(&fi->value); + label_fill_descr(label_str, hfinfo, + get_eui64_name(integer64), + eui64_to_str(integer64)); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, format_text(bytes, strlen(bytes))); + break; + + default: + g_error("hfinfo->type %d (%s) not handled\n", + hfinfo->type, ftype_name(hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } +} + +static void +fill_label_boolean(field_info *fi, gchar *label_str) +{ + char *p = label_str; + int bitfield_byte_length = 0, bitwidth; + guint32 unshifted_value; + guint32 value; + + header_field_info *hfinfo = fi->hfinfo; + const true_false_string *tfstring = (const true_false_string *)&tfs_true_false; + + if (hfinfo->strings) { + tfstring = (const struct true_false_string*) hfinfo->strings; + } + + value = fvalue_get_uinteger(&fi->value); + if (hfinfo->bitmask) { + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Un-shift bits */ + unshifted_value = value; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = (int) (p - label_str); + } + + /* Fill in the textual info */ + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, + value ? tfstring->true_string : tfstring->false_string); +} + +static const char * +hf_try_val_to_str(guint32 value, const header_field_info *hfinfo) +{ + if (hfinfo->display & BASE_RANGE_STRING) + return try_rval_to_str(value, (const range_string *) hfinfo->strings); + + if (hfinfo->display & BASE_EXT_STRING) + return try_val_to_str_ext(value, (const value_string_ext *) hfinfo->strings); + + return try_val_to_str(value, (const value_string *) hfinfo->strings); +} + +static const char * +hf_try_val_to_str_const(guint32 value, const header_field_info *hfinfo, const char *unknown_str) +{ + const char *str = hf_try_val_to_str(value, hfinfo); + + return (str) ? str : unknown_str; +} + +/* Fills data for bitfield ints with val_strings */ +static void +fill_label_bitfield(field_info *fi, gchar *label_str) +{ + char *p; + int bitfield_byte_length, bitwidth; + guint32 unshifted_value; + guint32 value; + + char buf[32]; + const char *out; + + header_field_info *hfinfo = fi->hfinfo; + + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Un-shift bits */ + unshifted_value = fvalue_get_uinteger(&fi->value); + value = unshifted_value; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = (int) (p - label_str); + + /* Fill in the textual info using stored (shifted) value */ + if (hfinfo->display == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, value); + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, tmp); + } + else if (hfinfo->strings) { + const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown"); + + out = hfinfo_number_vals_format(hfinfo, buf, value); + if (out == NULL) /* BASE_NONE so don't put integer in descr */ + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, val_str); + else + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s (%s)", hfinfo->name, val_str, out); + } + else { + out = hfinfo_number_value_format(hfinfo, buf, value); + + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, out); + } +} + +static void +fill_label_number(field_info *fi, gchar *label_str, gboolean is_signed) +{ + header_field_info *hfinfo = fi->hfinfo; + guint32 value; + + char buf[32]; + const char *out; + + if (is_signed) + value = fvalue_get_sinteger(&fi->value); + else + value = fvalue_get_uinteger(&fi->value); + + /* Fill in the textual info */ + if (hfinfo->display == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, value); + label_fill(label_str, hfinfo, tmp); + } + else if (hfinfo->strings) { + const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown"); + + out = hfinfo_number_vals_format(hfinfo, buf, value); + if (out == NULL) /* BASE_NONE so don't put integer in descr */ + label_fill(label_str, hfinfo, val_str); + else + label_fill_descr(label_str, hfinfo, val_str, out); + } + else { + out = hfinfo_number_value_format(hfinfo, buf, value); + + label_fill(label_str, hfinfo, out); + } +} + +static void +fill_label_number64(field_info *fi, gchar *label_str, gboolean is_signed) +{ + const char *format = NULL; + header_field_info *hfinfo = fi->hfinfo; + guint64 value; + + /* Pick the proper format string */ + if (is_signed) + format = hfinfo_int64_format(hfinfo); + else + format = hfinfo_uint64_format(hfinfo); + + value = fvalue_get_integer64(&fi->value); + + /* Fill in the textual info */ + if (IS_BASE_DUAL(hfinfo->display)) { + g_snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, value, value); + } else { + g_snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, value); + } +} + +int +hfinfo_bitwidth(const header_field_info *hfinfo) +{ + int bitwidth = 0; + + if (!hfinfo->bitmask) { + return 0; + } + + switch (hfinfo->type) { + case FT_UINT8: + case FT_INT8: + bitwidth = 8; + break; + case FT_UINT16: + case FT_INT16: + bitwidth = 16; + break; + case FT_UINT24: + case FT_INT24: + bitwidth = 24; + break; + case FT_UINT32: + case FT_INT32: + bitwidth = 32; + break; + case FT_BOOLEAN: + bitwidth = hfinfo->display; /* hacky? :) */ + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return bitwidth; +} + +static int +_hfinfo_type_hex_octet(int type) +{ + switch (type) { + case FT_INT8: + case FT_UINT8: + return 2; + + case FT_UINT16: + case FT_INT16: + return 4; + + case FT_UINT24: + case FT_INT24: + return 6; + + case FT_UINT32: + case FT_INT32: + return 8; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return -1; +} + +/* private to_str.c API don't export to .h! */ +char *oct_to_str_back(char *ptr, guint32 value); +char *hex_to_str_back(char *ptr, int pad, guint32 value); +char *uint_to_str_back(char *ptr, guint32 value); +char *int_to_str_back(char *ptr, gint32 value); + +static const char * +_hfinfo_number_value_format(const header_field_info *hfinfo, int display, char buf[32], guint32 value) +{ + char *ptr = &buf[31]; + gboolean isint = IS_FT_INT(hfinfo->type); + + *ptr = '\0'; + /* Properly format value */ + switch (display) { + case BASE_DEC: + return isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + + case BASE_DEC_HEX: + *(--ptr) = ')'; + ptr = hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + *(--ptr) = '('; + *(--ptr) = ' '; + ptr = isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + return ptr; + + case BASE_OCT: + return oct_to_str_back(ptr, value); + + case BASE_HEX: + return hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + + case BASE_HEX_DEC: + *(--ptr) = ')'; + ptr = isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + *(--ptr) = '('; + *(--ptr) = ' '; + ptr = hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + return ptr; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return ptr; +} + +static const char * +hfinfo_number_value_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + int display = hfinfo->display; + + if (hfinfo->type == FT_FRAMENUM) { + /* + * Frame numbers are always displayed in decimal. + */ + display = BASE_DEC; + } + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_numeric_value_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + /* Get the underlying BASE_ value */ + int display = hfinfo->display & BASE_DISPLAY_E_MASK; + + if (hfinfo->type == FT_FRAMENUM) { + /* + * Frame numbers are always displayed in decimal. + */ + display = BASE_DEC; + } + + switch (display) { + case BASE_NONE: + /* case BASE_DEC: */ + case BASE_DEC_HEX: + case BASE_OCT: /* XXX, why we're changing BASE_OCT to BASE_DEC? */ + case BASE_CUSTOM: + display = BASE_DEC; + break; + + /* case BASE_HEX: */ + case BASE_HEX_DEC: + display = BASE_HEX; + break; + } + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_number_vals_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + /* Get the underlying BASE_ value */ + int display = hfinfo->display & BASE_DISPLAY_E_MASK; + + if (display == BASE_NONE) + return NULL; + + if (display == BASE_DEC_HEX) + display = BASE_DEC; + if (display == BASE_HEX_DEC) + display = BASE_HEX; + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_uint64_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Pick the proper format string */ + switch (hfinfo->display) { + case BASE_DEC: + format = "%s: %" G_GINT64_MODIFIER "u"; + break; + case BASE_DEC_HEX: + format = "%s: %" G_GINT64_MODIFIER "u (0x%016" G_GINT64_MODIFIER "x)"; + break; + case BASE_OCT: /* I'm lazy */ + format = "%s: %#" G_GINT64_MODIFIER "o"; + break; + case BASE_HEX: + format = "%s: 0x%016" G_GINT64_MODIFIER "x"; + break; + case BASE_HEX_DEC: + format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "u)"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +static const char * +hfinfo_int64_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Pick the proper format string */ + switch (hfinfo->display) { + case BASE_DEC: + format = "%s: %" G_GINT64_MODIFIER "d"; + break; + case BASE_DEC_HEX: + format = "%s: %" G_GINT64_MODIFIER "d (0x%016" G_GINT64_MODIFIER "x)"; + break; + case BASE_OCT: /* I'm lazy */ + format = "%s: %#" G_GINT64_MODIFIER "o"; + break; + case BASE_HEX: + format = "%s: 0x%016" G_GINT64_MODIFIER "x"; + break; + case BASE_HEX_DEC: + format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "d)"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +int +proto_registrar_n(void) +{ + return gpa_hfinfo.len; +} + +const char * +proto_registrar_get_name(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->name; +} + +const char * +proto_registrar_get_abbrev(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->abbrev; +} + +enum ftenum +proto_registrar_get_ftype(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->type; +} + +int +proto_registrar_get_parent(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->parent; +} + +gboolean +proto_registrar_is_protocol(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return (hfinfo->parent == -1 ? TRUE : FALSE); +} + +/* Returns length of field in packet (not necessarily the length + * in our internal representation, as in the case of IPv4). + * 0 means undeterminable at time of registration + * -1 means the field is not registered. */ +gint +proto_registrar_get_length(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return ftype_length(hfinfo->type); +} + +/* Looks for a protocol or a field in a proto_tree. Returns TRUE if + * it exists anywhere, or FALSE if it exists nowhere. */ +gboolean +proto_check_for_protocol_or_field(const proto_tree* tree, const int id) +{ + GPtrArray *ptrs = proto_get_finfo_ptr_array(tree, id); + + if (!ptrs) { + return FALSE; + } + else if (g_ptr_array_len(ptrs) > 0) { + return TRUE; + } + else { + return FALSE; + } +} + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in tree. + * This only works if the hfindex was "primed" before the dissection + * took place, as we just pass back the already-created GPtrArray*. + * The caller should *not* free the GPtrArray*; proto_tree_free_node() + * handles that. */ +GPtrArray * +proto_get_finfo_ptr_array(const proto_tree *tree, const int id) +{ + if (!tree) + return NULL; + + if (PTREE_DATA(tree)->interesting_hfids != NULL) + return (GPtrArray *)g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids, + GINT_TO_POINTER(id)); + else + return NULL; +} + +gboolean +proto_tracking_interesting_fields(const proto_tree *tree) +{ + if (!tree) + return FALSE; + + return (PTREE_DATA(tree)->interesting_hfids != NULL); +} + +/* Helper struct for proto_find_info() and proto_all_finfos() */ +typedef struct { + GPtrArray *array; + int id; +} ffdata_t; + +/* Helper function for proto_find_info() */ +static gboolean +find_finfo(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + if (fi && fi->hfinfo) { + if (fi->hfinfo->id == ((ffdata_t*)data)->id) { + g_ptr_array_add(((ffdata_t*)data)->array, fi); + } + } + + /* Don't stop traversing. */ + return FALSE; +} + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in a tree. +* This works on any proto_tree, primed or unprimed, but actually searches +* the tree, so it is slower than using proto_get_finfo_ptr_array on a primed tree. +* The caller does need to free the returned GPtrArray with +* g_ptr_array_free(, TRUE). +*/ +GPtrArray * +proto_find_finfo(proto_tree *tree, const int id) +{ + ffdata_t ffdata; + + ffdata.array = g_ptr_array_new(); + ffdata.id = id; + + proto_tree_traverse_pre_order(tree, find_finfo, &ffdata); + + return ffdata.array; +} + +/* Helper function for proto_all_finfos() */ +static gboolean +every_finfo(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + if (fi && fi->hfinfo) { + g_ptr_array_add(((ffdata_t*)data)->array, fi); + } + + /* Don't stop traversing. */ + return FALSE; +} + +/* Return GPtrArray* of field_info pointers containing all hfindexes that appear in a tree. */ +GPtrArray * +proto_all_finfos(proto_tree *tree) +{ + ffdata_t ffdata; + + ffdata.array = g_ptr_array_new(); + ffdata.id = 0; + + proto_tree_traverse_pre_order(tree, every_finfo, &ffdata); + + return ffdata.array; +} + + +typedef struct { + guint offset; + field_info *finfo; + tvbuff_t *tvb; +} offset_search_t; + +static gboolean +check_for_offset(proto_node *node, const gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + offset_search_t *offsearch = (offset_search_t *)data; + + /* !fi == the top most container node which holds nothing */ + if (fi && !PROTO_ITEM_IS_HIDDEN(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) { + if (offsearch->offset >= (guint) fi->start && + offsearch->offset < (guint) (fi->start + fi->length)) { + + offsearch->finfo = fi; + return FALSE; /* keep traversing */ + } + } + return FALSE; /* keep traversing */ +} + +/* Search a proto_tree backwards (from leaves to root) looking for the field + * whose start/length occupies 'offset' */ +/* XXX - I couldn't find an easy way to search backwards, so I search + * forwards, w/o stopping. Therefore, the last finfo I find will the be + * the one I want to return to the user. This algorithm is inefficient + * and could be re-done, but I'd have to handle all the children and + * siblings of each node myself. When I have more time I'll do that. + * (yeah right) */ +field_info * +proto_find_field_from_offset(proto_tree *tree, guint offset, tvbuff_t *tvb) +{ + offset_search_t offsearch; + + offsearch.offset = offset; + offsearch.finfo = NULL; + offsearch.tvb = tvb; + + proto_tree_traverse_pre_order(tree, check_for_offset, &offsearch); + + return offsearch.finfo; +} + +/* Dumps the protocols in the registration database to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. The fields are tab-delimited. + * + * Field 1 = protocol name + * Field 2 = protocol short name + * Field 3 = protocol filter name + */ +void +proto_registrar_dump_protocols(void) +{ + protocol_t *protocol; + int i; + void *cookie = NULL; + + + i = proto_get_first_protocol(&cookie); + while (i != -1) { + protocol = find_protocol_by_id(i); + printf("%s\t%s\t%s\n", protocol->name, protocol->short_name, + protocol->filter_name); + i = proto_get_next_protocol(&cookie); + } +} + +/* Dumps the value_strings, extended value string headers, range_strings + * or true/false strings for fields that have them. + * There is one record per line. Fields are tab-delimited. + * There are four types of records: Value String, Extended Value String Header, + * Range String and True/False String. The first field, 'V', 'E', 'R' or 'T', indicates + * the type of record. + * + * Note that a record will be generated only if the value_string,... is referenced + * in a registered hfinfo entry. + * + * + * Value Strings + * ------------- + * Field 1 = 'V' + * Field 2 = Field abbreviation to which this value string corresponds + * Field 3 = Integer value + * Field 4 = String + * + * Extended Value String Headers + * ----------------------------- + * Field 1 = 'E' + * Field 2 = Field abbreviation to which this extended value string header corresponds + * Field 3 = Extended Value String "Name" + * Field 4 = Number of entries in the associated value_string array + * Field 5 = Access Type: "Linear Search", "Binary Search", "Direct (indexed) Access" + * + * Range Strings + * ------------- + * Field 1 = 'R' + * Field 2 = Field abbreviation to which this range string corresponds + * Field 3 = Integer value: lower bound + * Field 4 = Integer value: upper bound + * Field 5 = String + * + * True/False Strings + * ------------------ + * Field 1 = 'T' + * Field 2 = Field abbreviation to which this true/false string corresponds + * Field 3 = True String + * Field 4 = False String + */ +void +proto_registrar_dump_values(void) +{ + header_field_info *hfinfo; + int i, len, vi; + const value_string *vals; + const range_string *range; + const true_false_string *tfs; + + len = gpa_hfinfo.len; + for (i = 0; i < len ; i++) { + PROTO_REGISTRAR_GET_NTH(i, hfinfo); + + if (hfinfo->id == hf_text_only) { + continue; + } + + /* ignore protocols */ + if (proto_registrar_is_protocol(i)) { + continue; + } + /* process header fields */ + else { + /* + * If this field isn't at the head of the list of + * fields with this name, skip this field - all + * fields with the same name are really just versions + * of the same field stored in different bits, and + * should have the same type/radix/value list, and + * just differ in their bit masks. (If a field isn't + * a bitfield, but can be, say, 1 or 2 bytes long, + * it can just be made FT_UINT16, meaning the + * *maximum* length is 2 bytes, and be used + * for all lengths.) + */ + if (hfinfo->same_name_prev != NULL) + continue; + + vals = NULL; + range = NULL; + tfs = NULL; + + if (hfinfo->strings != NULL) { + if ((hfinfo->display & BASE_DISPLAY_E_MASK) != BASE_CUSTOM && + (hfinfo->type == FT_UINT8 || + hfinfo->type == FT_UINT16 || + hfinfo->type == FT_UINT24 || + hfinfo->type == FT_UINT32 || + hfinfo->type == FT_UINT64 || + hfinfo->type == FT_INT8 || + hfinfo->type == FT_INT16 || + hfinfo->type == FT_INT24 || + hfinfo->type == FT_INT32 || + hfinfo->type == FT_INT64)) { + + if (hfinfo->display & BASE_EXT_STRING) { + vals = VALUE_STRING_EXT_VS_P((const value_string_ext *)hfinfo->strings); + } else if ((hfinfo->display & BASE_RANGE_STRING) == 0) { + vals = (const value_string *)hfinfo->strings; + } else { + range = (const range_string *)hfinfo->strings; + } + } + else if (hfinfo->type == FT_BOOLEAN) { + tfs = (const struct true_false_string *)hfinfo->strings; + } + } + + /* Print value strings? */ + if (vals) { + if (hfinfo->display & BASE_EXT_STRING) { + const value_string_ext *vse_p = (const value_string_ext *)hfinfo->strings; + if (!value_string_ext_validate(vse_p)) { + g_warning("Invalid value_string_ext ptr for: %s", hfinfo->abbrev); + continue; + } + try_val_to_str_ext(0, vse_p); /* "prime" the extended value_string */ + printf("E\t%s\t%d\t%s\t%s\n", + hfinfo->abbrev, + VALUE_STRING_EXT_VS_NUM_ENTRIES(vse_p), + VALUE_STRING_EXT_VS_NAME(vse_p), + value_string_ext_match_type_str(vse_p)); + } + vi = 0; + while (vals[vi].strptr) { + /* Print in the proper base */ + if (hfinfo->display == BASE_HEX) { + printf("V\t%s\t0x%x\t%s\n", + hfinfo->abbrev, + vals[vi].value, + vals[vi].strptr); + } + else { + printf("V\t%s\t%u\t%s\n", + hfinfo->abbrev, + vals[vi].value, + vals[vi].strptr); + } + vi++; + } + } + + /* print range strings? */ + else if (range) { + vi = 0; + while (range[vi].strptr) { + /* Print in the proper base */ + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_HEX) { + printf("R\t%s\t0x%x\t0x%x\t%s\n", + hfinfo->abbrev, + range[vi].value_min, + range[vi].value_max, + range[vi].strptr); + } + else { + printf("R\t%s\t%u\t%u\t%s\n", + hfinfo->abbrev, + range[vi].value_min, + range[vi].value_max, + range[vi].strptr); + } + vi++; + } + } + + /* Print true/false strings? */ + else if (tfs) { + printf("T\t%s\t%s\t%s\n", hfinfo->abbrev, + tfs->true_string, tfs->false_string); + } + } + } +} + +/* Dumps the contents of the registration database to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. Each record is either a protocol or a header + * field, differentiated by the first field. The fields are tab-delimited. + * + * Protocols + * --------- + * Field 1 = 'P' + * Field 2 = descriptive protocol name + * Field 3 = protocol abbreviation + * + * Header Fields + * ------------- + * Field 1 = 'F' + * Field 2 = descriptive field name + * Field 3 = field abbreviation + * Field 4 = type ( textual representation of the the ftenum type ) + * Field 5 = parent protocol abbreviation + * Field 6 = base for display (for integer types); "parent bitfield width" for FT_BOOLEAN + * Field 7 = bitmask: format: hex: 0x.... + * Field 8 = blurb describing field + */ +void +proto_registrar_dump_fields(void) +{ + header_field_info *hfinfo, *parent_hfinfo; + int i, len; + const char *enum_name; + const char *base_name; + const char *blurb; + char width[5]; + + len = gpa_hfinfo.len; + for (i = 0; i < len ; i++) { + PROTO_REGISTRAR_GET_NTH(i, hfinfo); + + /* + * Skip the pseudo-field for "proto_tree_add_text()" since + * we don't want it in the list of filterable fields. + */ + if (hfinfo->id == hf_text_only) + continue; + + /* format for protocols */ + if (proto_registrar_is_protocol(i)) { + printf("P\t%s\t%s\n", hfinfo->name, hfinfo->abbrev); + } + /* format for header fields */ + else { + /* + * If this field isn't at the head of the list of + * fields with this name, skip this field - all + * fields with the same name are really just versions + * of the same field stored in different bits, and + * should have the same type/radix/value list, and + * just differ in their bit masks. (If a field isn't + * a bitfield, but can be, say, 1 or 2 bytes long, + * it can just be made FT_UINT16, meaning the + * *maximum* length is 2 bytes, and be used + * for all lengths.) + */ + if (hfinfo->same_name_prev != NULL) + continue; + + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + + enum_name = ftype_name(hfinfo->type); + base_name = ""; + + if (hfinfo->type == FT_UINT8 || + hfinfo->type == FT_UINT16 || + hfinfo->type == FT_UINT24 || + hfinfo->type == FT_UINT32 || + hfinfo->type == FT_UINT64 || + hfinfo->type == FT_INT8 || + hfinfo->type == FT_INT16 || + hfinfo->type == FT_INT24 || + hfinfo->type == FT_INT32 || + hfinfo->type == FT_INT64) { + + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_NONE: + base_name = "BASE_NONE"; + break; + case BASE_DEC: + base_name = "BASE_DEC"; + break; + case BASE_HEX: + base_name = "BASE_HEX"; + break; + case BASE_OCT: + base_name = "BASE_OCT"; + break; + case BASE_DEC_HEX: + base_name = "BASE_DEC_HEX"; + break; + case BASE_HEX_DEC: + base_name = "BASE_HEX_DEC"; + break; + case BASE_CUSTOM: + base_name = "BASE_CUSTOM"; + break; + default: + base_name = "????"; + break; + } + } else if (hfinfo->type == FT_BOOLEAN) { + /* For FT_BOOLEAN: 'display' can be "parent bitfield width" */ + g_snprintf(width, sizeof(width), "%d", hfinfo->display); + base_name = width; + } + + blurb = hfinfo->blurb; + if (blurb == NULL) + blurb = ""; + else if (strlen(blurb) == 0) + blurb = "\"\""; + + printf("F\t%s\t%s\t%s\t%s\t%s\t0x%x\t%s\n", + hfinfo->name, hfinfo->abbrev, enum_name, + parent_hfinfo->abbrev, base_name, hfinfo->bitmask, blurb); + } + } +} + +/* Dumps field types and descriptive names to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. The fields are tab-delimited. + * + * Field 1 = field type name, e.g. FT_UINT8 + * Field 2 = descriptive name, e.g. "Unsigned, 1 byte" + */ +void +proto_registrar_dump_ftypes(void) +{ + int fte; + + for (fte = 0; fte < FT_NUM_TYPES; fte++) { + printf("%s\t%s\n", ftype_name((ftenum_t)fte), ftype_pretty_name((ftenum_t)fte)); + } +} + +static const char * +hfinfo_numeric_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Get the underlying BASE_ value */ + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_DEC: + case BASE_DEC_HEX: + case BASE_OCT: /* I'm lazy */ + case BASE_CUSTOM: + switch (hfinfo->type) { + case FT_UINT64: + format = "%s == %" G_GINT64_MODIFIER "u"; + break; + case FT_INT64: + format = "%s == %" G_GINT64_MODIFIER "d"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + break; + case BASE_HEX: + case BASE_HEX_DEC: + switch (hfinfo->type) { + case FT_UINT64: + case FT_INT64: + format = "%s == 0x%016" G_GINT64_MODIFIER "x"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +/* This function indicates whether it's possible to construct a + * "match selected" display filter string for the specified field, + * returns an indication of whether it's possible, and, if it's + * possible and "filter" is non-null, constructs the filter and + * sets "*filter" to point to it. + * You do not need to [g_]free() this string since it will be automatically + * freed once the next packet is dissected. + */ +static gboolean +construct_match_selected_string(field_info *finfo, epan_dissect_t *edt, + char **filter) +{ + header_field_info *hfinfo; + int abbrev_len; + char *ptr; + int buf_len; + int dfilter_len, i; + gint start, length, length_remaining; + guint8 c; + gchar is_signed_num = FALSE; + + if (!finfo) + return FALSE; + + hfinfo = finfo->hfinfo; + DISSECTOR_ASSERT(hfinfo); + abbrev_len = (int) strlen(hfinfo->abbrev); + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + const gchar *str = NULL; + + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + str = hf_try_val_to_str(fvalue_get_sinteger(&finfo->value), hfinfo); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + str = hf_try_val_to_str(fvalue_get_uinteger(&finfo->value), hfinfo); + break; + + default: + break; + } + + if (str != NULL && filter != NULL) { + *filter = ep_strdup_printf("%s == \"%s\"", hfinfo->abbrev, str); + return TRUE; + } + } + + /* + * XXX - we can't use the "val_to_string_repr" and "string_repr_len" + * functions for FT_UINT and FT_INT types, as we choose the base in + * the string expression based on the display base of the field. + * + * Note that the base does matter, as this is also used for + * the protocolinfo tap. + * + * It might be nice to use them in "proto_item_fill_label()" + * as well, although, there, you'd have to deal with the base + * *and* with resolved values for addresses. + * + * Perhaps we need two different val_to_string routines, one + * to generate items for display filters and one to generate + * strings for display, and pass to both of them the + * "display" and "strings" values in the header_field_info + * structure for the field, so they can get the base and, + * if the field is Boolean or an enumerated integer type, + * the tables used to generate human-readable values. + */ + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + is_signed_num = TRUE; + /* FALLTHRU */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + if (filter != NULL) { + guint32 number; + + char buf[32]; + const char *out; + + if (is_signed_num) + number = fvalue_get_sinteger(&finfo->value); + else + number = fvalue_get_uinteger(&finfo->value); + + out = hfinfo_numeric_value_format(hfinfo, buf, number); + + *filter = ep_strdup_printf("%s == %s", hfinfo->abbrev, out); + } + break; + + case FT_INT64: + case FT_UINT64: + if (filter != NULL) { + const char *format = hfinfo_numeric_format(hfinfo); + + *filter = ep_strdup_printf(format, + hfinfo->abbrev, + fvalue_get_integer64(&finfo->value)); + } + break; + + case FT_PROTOCOL: + if (filter != NULL) + *filter = ep_strdup(finfo->hfinfo->abbrev); + break; + + case FT_NONE: + /* + * If the length is 0, just match the name of the + * field. + * + * (Also check for negative values, just in case, + * as we'll cast it to an unsigned value later.) + */ + length = finfo->length; + if (length == 0) { + if (filter != NULL) + *filter = ep_strdup(finfo->hfinfo->abbrev); + break; + } + if (length < 0) + return FALSE; + + /* + * This doesn't have a value, so we'd match + * on the raw bytes at this address. + * + * Should we be allowed to access to the raw bytes? + * If "edt" is NULL, the answer is "no". + */ + if (edt == NULL) + return FALSE; + + /* + * Is this field part of the raw frame tvbuff? + * If not, we can't use "frame[N:M]" to match + * it. + * + * XXX - should this be frame-relative, or + * protocol-relative? + * + * XXX - does this fallback for non-registered + * fields even make sense? + */ + if (finfo->ds_tvb != edt->tvb) + return FALSE; /* you lose */ + + /* + * Don't go past the end of that tvbuff. + */ + length_remaining = tvb_length_remaining(finfo->ds_tvb, finfo->start); + if (length > length_remaining) + length = length_remaining; + if (length <= 0) + return FALSE; + + if (filter != NULL) { + start = finfo->start; + buf_len = 32 + length * 3; + *filter = (char *)ep_alloc0(buf_len); + ptr = *filter; + + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), + "frame[%d:%d] == ", finfo->start, length); + for (i=0; ids_tvb, start); + start++; + if (i == 0 ) { + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), "%02x", c); + } + else { + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), ":%02x", c); + } + } + } + break; + + case FT_PCRE: + /* FT_PCRE never appears as a type for a registered field. It is + * only used internally. */ + DISSECTOR_ASSERT_NOT_REACHED(); + break; + + /* By default, use the fvalue's "to_string_repr" method. */ + default: + /* Figure out the string length needed. + * The ft_repr length. + * 4 bytes for " == ". + * 1 byte for trailing NUL. + */ + if (filter != NULL) { + dfilter_len = fvalue_string_repr_len(&finfo->value, + FTREPR_DFILTER); + dfilter_len += abbrev_len + 4 + 1; + *filter = (char *)ep_alloc0(dfilter_len); + + /* Create the string */ + g_snprintf(*filter, dfilter_len, "%s == ", + hfinfo->abbrev); + fvalue_to_string_repr(&finfo->value, + FTREPR_DFILTER, + &(*filter)[abbrev_len + 4]); + } + break; + } + + return TRUE; +} + +/* + * Returns TRUE if we can do a "match selected" on the field, FALSE + * otherwise. + */ +gboolean +proto_can_match_selected(field_info *finfo, epan_dissect_t *edt) +{ + return construct_match_selected_string(finfo, edt, NULL); +} + +/* This function attempts to construct a "match selected" display filter + * string for the specified field; if it can do so, it returns a pointer + * to the string, otherwise it returns NULL. + * + * The string is allocated with packet lifetime scope. + * You do not need to [g_]free() this string since it will be automatically + * freed once the next packet is dissected. + */ +char * +proto_construct_match_selected_string(field_info *finfo, epan_dissect_t *edt) +{ + char *filter; + + if (!construct_match_selected_string(finfo, edt, &filter)) + return NULL; + return filter; +} + +/* This function is common code for both proto_tree_add_bitmask() and + * proto_tree_add_bitmask_text() functions. + */ + +/* NOTE: to support code written when proto_tree_add_bitmask() and + * proto_tree_add_bitmask_text took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static gboolean +proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset, + const int len, const gint ett, const int **fields, + const guint encoding, const int flags, + gboolean first) +{ + guint32 value = 0; + guint32 available_bits = 0; + guint32 tmpval; + proto_tree *tree = NULL; + header_field_info *hf; + + switch (len) { + case 1: + value = tvb_get_guint8(tvb, offset); + available_bits = 0xFF; + break; + case 2: + value = encoding ? tvb_get_letohs(tvb, offset) : + tvb_get_ntohs(tvb, offset); + available_bits = 0xFFFF; + break; + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) : + tvb_get_ntoh24(tvb, offset); + available_bits = 0xFFFFFF; + break; + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) : + tvb_get_ntohl(tvb, offset); + available_bits = 0xFFFFFFFF; + break; + default: + g_assert_not_reached(); + } + + tree = proto_item_add_subtree(item, ett); + while (*fields) { + guint32 present_bits; + hf = proto_registrar_get_nth(**fields); + DISSECTOR_ASSERT(hf->bitmask != 0); + + /* Skip fields that aren't fully present */ + present_bits = available_bits & hf->bitmask; + if (present_bits != hf->bitmask) { + fields++; + continue; + } + + proto_tree_add_item(tree, **fields, tvb, offset, len, encoding); + if (flags & BMT_NO_APPEND) { + fields++; + continue; + } + tmpval = (value & hf->bitmask) >> hf->bitshift; + + switch (hf->type) { + case FT_INT8: + case FT_UINT8: + case FT_INT16: + case FT_UINT16: + case FT_INT24: + case FT_UINT24: + case FT_INT32: + case FT_UINT32: + if (hf->display == BASE_CUSTOM) { + gchar lbl[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hf->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(lbl, tmpval); + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, lbl); + first = FALSE; + } + else if (hf->strings) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, hf_try_val_to_str_const(tmpval, hf, "Unknown")); + first = FALSE; + } + else if (!(flags & BMT_NO_INT)) { + char buf[32]; + const char *out; + + if (!first) { + proto_item_append_text(item, ", "); + } + + out = hfinfo_number_value_format(hf, buf, tmpval); + proto_item_append_text(item, "%s: %s", hf->name, out); + first = FALSE; + } + + break; + case FT_BOOLEAN: + if (hf->strings && !(flags & BMT_NO_TFS)) { + /* If we have true/false strings, emit full - otherwise messages + might look weird */ + const struct true_false_string *tfs = + (const struct true_false_string *)hf->strings; + + if (tmpval) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, tfs->true_string); + first = FALSE; + } else if (!(flags & BMT_NO_FALSE)) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, tfs->false_string); + first = FALSE; + } + } else if (hf->bitmask & value) { + /* If the flag is set, show the name */ + proto_item_append_text(item, "%s%s", first ? "" : ", ", hf->name); + first = FALSE; + } + break; + default: + g_assert_not_reached(); + } + + fields++; + } + + return first; +} + +/* This function will dissect a sequence of bytes that describe a + * bitmask. + * hf_hdr is a 8/16/24/32 bit integer that describes the bitmask to be dissected. + * This field will form an expansion under which the individual fields of the + * bitmask is dissected and displayed. + * This field must be of the type FT_[U]INT{8|16|24|32}. + * + * fields is an array of pointers to int that lists all the fields of the + * bitmask. These fields can be either of the type FT_BOOLEAN for flags + * or another integer of the same type/size as hf_hdr with a mask specified. + * This array is terminated by a NULL entry. + * + * FT_BOOLEAN bits that are set to 1 will have the name added to the expansion. + * FT_integer fields that have a value_string attached will have the + * matched string displayed on the expansion line. + */ +proto_item * +proto_tree_add_bitmask(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const int hf_hdr, + const gint ett, const int **fields, + const guint encoding) +{ + proto_item *item = NULL; + header_field_info *hf; + int len; + + hf = proto_registrar_get_nth(hf_hdr); + DISSECTOR_ASSERT(IS_FT_INT(hf->type) || IS_FT_UINT(hf->type)); + len = ftype_length(hf->type); + + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_hdr, tvb, offset, len, encoding); + proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, encoding, + BMT_NO_INT|BMT_NO_TFS, FALSE); + } + + return item; +} + +/* The same as proto_tree_add_bitmask(), but using a caller-supplied length. + * This is intended to support bitmask fields whose lengths can vary, perhaps + * as the underlying standard evolves over time. + * With this API there is the possibility of being called to display more or + * less data than the dissector was coded to support. + * In such cases, it is assumed that bitmasks are extended on the MSb end. + * Thus when presented with "too much" or "too little" data, MSbits will be + * ignored or MSfields sacrificed. + * + * Only fields for which all defined bits are available are displayed. + */ +proto_item * +proto_tree_add_bitmask_len(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const guint len, const int hf_hdr, + const gint ett, const int **fields, + const guint encoding) +{ + proto_item *item = NULL; + header_field_info *hf; + + hf = proto_registrar_get_nth(hf_hdr); + DISSECTOR_ASSERT(IS_FT_INT(hf->type) || IS_FT_UINT(hf->type)); + + if (parent_tree) { + guint decodable_len; + guint decodable_offset; + guint32 decodable_value; + + decodable_offset = offset; + decodable_len = MIN(len, (guint) ftype_length(hf->type)); + + /* If we are ftype_length-limited, + * make sure we decode as many LSBs as possible. + */ + if (encoding == ENC_BIG_ENDIAN) { + decodable_offset += (len - decodable_len); + } + + decodable_value = get_uint_value(parent_tree, tvb, decodable_offset, + decodable_len, encoding); + + /* The root item covers all the bytes even if we can't decode them all */ + item = proto_tree_add_uint(parent_tree, hf_hdr, tvb, offset, len, + decodable_value); + + if (decodable_len < len) { + /* Dissector likely requires updating for new protocol revision */ + expert_add_info_format(NULL, item, PI_UNDECODED, PI_WARN, + "Only least-significant %d of %d bytes decoded", + decodable_len, len); + } + + proto_item_add_bitmask_tree(item, tvb, decodable_offset, decodable_len, + ett, fields, encoding, BMT_NO_INT|BMT_NO_TFS, FALSE); + } + + return item; +} + +/* The same as proto_tree_add_bitmask(), but using an arbitrary text as a top-level item */ +proto_item * +proto_tree_add_bitmask_text(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const guint len, + const char *name, const char *fallback, + const gint ett, const int **fields, + const guint encoding, const int flags) +{ + proto_item *item = NULL; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, len, "%s", name ? name : ""); + if (proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, encoding, + flags, TRUE) && fallback) { + /* Still at first item - append 'fallback' text if any */ + proto_item_append_text(item, "%s", fallback); + } + } + + return item; +} + +proto_item * +proto_tree_add_bits_item(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + const guint encoding) +{ + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hfinfo); + + return proto_tree_add_bits_ret_val(tree, hf_index, tvb, bit_offset, no_of_bits, NULL, encoding); +} + +/* + * This function will dissect a sequence of bits that does not need to be byte aligned; the bits + * set will be shown in the tree as ..10 10.. and the integer value returned if return_value is set. + * Offset should be given in bits from the start of the tvb. + */ + +static proto_item * +_proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + guint64 *return_value, const guint encoding) +{ + gint offset; + guint length; + guint8 tot_no_bits; + char *bf_str; + char lbl_str[ITEM_LABEL_LENGTH]; + guint64 value = 0; + + proto_item *pi; + header_field_info *hf_field; + + const true_false_string *tfstring; + + /* We can't fake it just yet. We have to fill in the 'return_value' parameter */ + PROTO_REGISTRAR_GET_NTH(hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf("Incompatible use of proto_tree_add_bits_ret_val" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + DISSECTOR_ASSERT(no_of_bits > 0); + + /* Byte align offset */ + offset = bit_offset>>3; + + /* + * Calculate the number of octets used to hold the bits + */ + tot_no_bits = ((bit_offset&0x7) + no_of_bits); + length = tot_no_bits>>3; + /* If we are using part of the next octet, increase length by 1 */ + if (tot_no_bits & 0x07) + length++; + + if (no_of_bits < 65) { + value = tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding); + } else { + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + } + + /* Sign extend for signed types */ + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + if (value & (G_GINT64_CONSTANT(1) << (no_of_bits-1))) + value |= (G_GINT64_CONSTANT(-1) << no_of_bits); + break; + + default: + break; + } + + if (return_value) { + *return_value = value; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + bf_str = decode_bits_in_field(bit_offset, no_of_bits, value); + + switch (hf_field->type) { + case FT_BOOLEAN: + /* Boolean field */ + tfstring = (const true_false_string *) &tfs_true_false; + if (hf_field->strings) + tfstring = (const true_false_string *)hf_field->strings; + return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, (guint32)value, + "%s = %s: %s", + bf_str, hf_field->name, + (guint32)value ? tfstring->true_string : tfstring->false_string); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + pi = proto_tree_add_uint(tree, hf_index, tvb, offset, length, (guint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_int(tree, hf_index, tvb, offset, length, (gint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + case FT_UINT64: + pi = proto_tree_add_uint64(tree, hf_index, tvb, offset, length, value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT64: + pi = proto_tree_add_int64(tree, hf_index, tvb, offset, length, (gint64)value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + proto_item_set_text(pi, "%s = %s", bf_str, lbl_str); + return pi; +} + +proto_item * +proto_tree_add_split_bits_item_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const crumb_spec_t *crumb_spec, + guint64 *return_value) +{ + proto_item *pi; + gint no_of_bits; + gint octet_offset; + guint mask_initial_bit_offset; + guint mask_greatest_bit_offset; + guint octet_length; + guint8 i; + char *bf_str; + char lbl_str[ITEM_LABEL_LENGTH]; + guint64 value; + guint64 composite_bitmask; + guint64 composite_bitmap; + + header_field_info *hf_field; + const true_false_string *tfstring; + + /* We can't fake it just yet. We have to fill in the 'return_value' parameter */ + PROTO_REGISTRAR_GET_NTH(hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf( + "Incompatible use of proto_tree_add_split_bits_item_ret_val" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + mask_initial_bit_offset = bit_offset % 8; + + no_of_bits = 0; + value = 0; + i = 0; + mask_greatest_bit_offset = 0; + composite_bitmask = 0; + composite_bitmap = 0; + + while (crumb_spec[i].crumb_bit_length != 0) { + guint64 crumb_mask, crumb_value; + guint8 crumb_end_bit_offset; + + DISSECTOR_ASSERT(i < 64); + crumb_value = tvb_get_bits64(tvb, + bit_offset + crumb_spec[i].crumb_bit_offset, + crumb_spec[i].crumb_bit_length, + ENC_BIG_ENDIAN); + value += crumb_value; + no_of_bits += crumb_spec[i].crumb_bit_length; + + /* The bitmask is 64 bit, left-aligned, starting at the first bit of the + octet containing the initial offset. + If the mask is beyond 32 bits, then give up on bit map display. + This could be improved in future, probably showing a table + of 32 or 64 bits per row */ + if (mask_greatest_bit_offset < 32) { + crumb_end_bit_offset = mask_initial_bit_offset + + crumb_spec[i].crumb_bit_offset + + crumb_spec[i].crumb_bit_length; + crumb_mask = (G_GUINT64_CONSTANT(1) << crumb_spec[i].crumb_bit_length) - 1; + + if (crumb_end_bit_offset > mask_greatest_bit_offset) { + mask_greatest_bit_offset = crumb_end_bit_offset; + } + composite_bitmask |= (crumb_mask << (64 - crumb_end_bit_offset)); + composite_bitmap |= (crumb_value << (64 - crumb_end_bit_offset)); + } + /* Shift left for the next segment */ + value <<= crumb_spec[++i].crumb_bit_length; + } + + /* Sign extend for signed types */ + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + if (no_of_bits && (value & (G_GINT64_CONSTANT(1) << (no_of_bits-1)))) + value |= (G_GINT64_CONSTANT(-1) << no_of_bits); + break; + default: + break; + } + + if (return_value) { + *return_value = value; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + /* initialise the format string */ + bf_str = (char *)ep_alloc(256); + bf_str[0] = '\0'; + + octet_offset = bit_offset >> 3; + + /* Round up mask length to nearest octet */ + octet_length = ((mask_greatest_bit_offset + 7) >> 3); + mask_greatest_bit_offset = octet_length << 3; + + /* As noted above, we currently only produce a bitmap if the crumbs span less than 4 octets of the tvb. + It would be a useful enhancement to eliminate this restriction. */ + if (mask_greatest_bit_offset <= 32) { + other_decode_bitfield_value(bf_str, + (guint32)(composite_bitmap >> (64 - mask_greatest_bit_offset)), + (guint32)(composite_bitmask >> (64 - mask_greatest_bit_offset)), + mask_greatest_bit_offset); + } + + switch (hf_field->type) { + case FT_BOOLEAN: /* it is a bit odd to have a boolean encoded as split-bits, but possible, I suppose? */ + /* Boolean field */ + tfstring = (const true_false_string *) &tfs_true_false; + if (hf_field->strings) + tfstring = (const true_false_string *) hf_field->strings; + return proto_tree_add_boolean_format(tree, hf_index, + tvb, octet_offset, octet_length, (guint32)value, + "%s = %s: %s", + bf_str, hf_field->name, + (guint32)value ? tfstring->true_string : tfstring->false_string); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + pi = proto_tree_add_uint(tree, hf_index, tvb, octet_offset, octet_length, (guint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_int(tree, hf_index, tvb, octet_offset, octet_length, (gint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + case FT_UINT64: + pi = proto_tree_add_uint64(tree, hf_index, tvb, octet_offset, octet_length, value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT64: + pi = proto_tree_add_int64(tree, hf_index, tvb, octet_offset, octet_length, (gint64)value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + proto_item_set_text(pi, "%s = %s", bf_str, lbl_str); + return pi; +} + +void +proto_tree_add_split_bits_crumb(proto_tree *tree, const int hf_index, tvbuff_t *tvb, const guint bit_offset, + const crumb_spec_t *crumb_spec, guint16 crumb_index) +{ + header_field_info *hf_info; + + PROTO_REGISTRAR_GET_NTH(hf_index, hf_info); + proto_tree_add_text(tree, tvb, + bit_offset >> 3, + ((bit_offset + crumb_spec[crumb_index].crumb_bit_length - 1) >> 3) - (bit_offset >> 3) + 1, + "%s crumb %d of %s (decoded above)", + decode_bits_in_field(bit_offset, crumb_spec[crumb_index].crumb_bit_length, + tvb_get_bits(tvb, + bit_offset, + crumb_spec[crumb_index].crumb_bit_length, + ENC_BIG_ENDIAN)), + crumb_index, + hf_info->name); +} + +proto_item * +proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + guint64 *return_value, const guint encoding) +{ + proto_item *item; + + if ((item = _proto_tree_add_bits_ret_val(tree, hf_index, tvb, + bit_offset, no_of_bits, + return_value, encoding))) { + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset)); + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits)); + } + return item; +} + +static proto_item * +_proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, void *value_ptr, + gchar *value_str) +{ + gint offset; + guint length; + guint8 tot_no_bits; + char *str; + guint64 value = 0; + header_field_info *hf_field; + + /* We do not have to return a value, try to fake it as soon as possible */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf( + "Incompatible use of proto_tree_add_bits_format_value" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + DISSECTOR_ASSERT(no_of_bits > 0); + + /* Byte align offset */ + offset = bit_offset>>3; + + /* + * Calculate the number of octets used to hold the bits + */ + tot_no_bits = ((bit_offset&0x7) + no_of_bits); + length = tot_no_bits>>3; + /* If we are using part of the next octet, increase length by 1 */ + if (tot_no_bits & 0x07) + length++; + + if (no_of_bits < 65) { + value = tvb_get_bits64(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN); + } else { + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + } + + str = decode_bits_in_field(bit_offset, no_of_bits, value); + + strcat(str, " = "); + strcat(str, hf_field->name); + + /* + * This function does not receive an actual value but a dimensionless pointer to that value. + * For this reason, the type of the header field is examined in order to determine + * what kind of value we should read from this address. + * The caller of this function must make sure that for the specific header field type the address of + * a compatible value is provided. + */ + switch (hf_field->type) { + case FT_BOOLEAN: + return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_UINT64: + return proto_tree_add_uint64_format(tree, hf_index, tvb, offset, length, *(guint64 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + return proto_tree_add_int_format(tree, hf_index, tvb, offset, length, *(gint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_INT64: + return proto_tree_add_int64_format(tree, hf_index, tvb, offset, length, *(gint64 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_FLOAT: + return proto_tree_add_float_format(tree, hf_index, tvb, offset, length, *(float *)value_ptr, + "%s: %s", str, value_str); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } +} + +static proto_item * +proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, void *value_ptr, + gchar *value_str) +{ + proto_item *item; + + if ((item = _proto_tree_add_bits_format_value(tree, hf_index, + tvb, bit_offset, no_of_bits, + value_ptr, value_str))) { + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset)); + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits)); + } + return item; +} + +#define CREATE_VALUE_STRING(dst,format,ap) \ + va_start(ap, format); \ + dst = ep_strdup_vprintf(format, ap); \ + va_end(ap); + +proto_item * +proto_tree_add_uint_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, guint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + switch (hf_field->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_float_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, float value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + DISSECTOR_ASSERT(hf_field->type == FT_FLOAT); + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_int_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, gint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_boolean_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, guint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + DISSECTOR_ASSERT(hf_field->type == FT_BOOLEAN); + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +guchar +proto_check_field_name(const gchar *field_name) +{ + return wrs_check_charset(fld_abbrev_chars, field_name); +} + +gboolean +tree_expanded(int tree_type) +{ + g_assert(tree_type >= 0 && tree_type < num_tree_types); + return tree_is_expanded[tree_type >> 5] & (1 << (tree_type & 31)); +} + +void +tree_expanded_set(int tree_type, gboolean value) +{ + g_assert(tree_type >= 0 && tree_type < num_tree_types); + + if (value) + tree_is_expanded[tree_type >> 5] |= (1 << (tree_type & 31)); + else + tree_is_expanded[tree_type >> 5] &= ~(1 << (tree_type & 31)); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/main/resource/testFiles/wireshark/prevFiles/prev_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c b/src/main/resource/testFiles/wireshark/prevFiles/prev_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c new file mode 100644 index 0000000..a629419 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/prevFiles/prev_96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c @@ -0,0 +1,12477 @@ +/* packet-gsm_a_rr.c + * Routines for GSM A Interface (actually A-bis really) RR dissection - A.K.A. GSM layer 3 Radio Resource Protocol + * + * Copyright 2003, Michael Lum + * In association with Telos Technology Inc. + * + * Added Dissection of Radio Resource Management Information Elements + * and other enhancements and fixes. + * Copyright 2005 - 2006, Anders Broman [AT] ericsson.com + * + * Title 3GPP Other + * + * Reference [3] + * Mobile radio interface Layer 3 specification; + * Core network protocols; + * Stage 3 + * (3GPP TS 24.008 version 4.7.0 Release 4) + * (ETSI TS 124 008 V6.8.0 (2005-03)) + * + * Reference [4] + * Mobile radio interface layer 3 specification; + * Radio Resource Control Protocol + * (GSM 04.18 version 8.4.1 Release 1999) + * (3GPP TS 04.18 version 8.26.0 Release 1999) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "packet-bssap.h" +#include "packet-sccp.h" +#include "packet-ber.h" +#include "packet-q931.h" +#include "packet-gsm_a_common.h" +#include "packet-e212.h" +#include "packet-ppp.h" + +static dissector_handle_t rrc_irat_ho_info_handle; +static dissector_handle_t rrc_irat_ho_to_utran_cmd_handle; + +#define PADDING_BYTE 0x2B + +/* PROTOTYPES/FORWARDS */ + +const value_string gsm_a_dtap_msg_rr_strings[] = { + { 0x3c, "Reserved" }, + { 0x3b, "Additional Assignment" }, + { 0x3f, "Immediate Assignment" }, + { 0x39, "Immediate Assignment Extended" }, + { 0x3a, "Immediate Assignment Reject" }, + + { 0x48, "DTM Assignment Failure" }, + { 0x49, "DTM Reject" }, + { 0x4a, "DTM Request" }, + { 0x4b, "Main DCCH Assignment Command" }, + { 0x4c, "Packet Assignment Command" }, + + { 0x35, "Ciphering Mode Command" }, + { 0x32, "Ciphering Mode Complete" }, + + { 0x30, "Configuration Change Command" }, + { 0x31, "Configuration Change Ack." }, + { 0x33, "Configuration Change Reject" }, + + { 0x2e, "Assignment Command" }, + { 0x29, "Assignment Complete" }, + { 0x2f, "Assignment Failure" }, + { 0x2b, "Handover Command" }, + { 0x2c, "Handover Complete" }, + { 0x28, "Handover Failure" }, + { 0x2d, "Physical Information" }, + { 0x4d, "DTM Assignment Command" }, + + { 0x08, "RR-cell Change Order" }, + { 0x23, "PDCH Assignment Command" }, + + { 0x0d, "Channel Release" }, + { 0x0a, "Partial Release" }, + { 0x0f, "Partial Release Complete" }, + + { 0x21, "Paging Request Type 1" }, + { 0x22, "Paging Request Type 2" }, + { 0x24, "Paging Request Type 3" }, + { 0x27, "Paging Response" }, + { 0x20, "Notification/NCH" }, + { 0x25, "Reserved" }, + { 0x26, "Notification/Response" }, + + { 0x0b, "Reserved" }, + +#if 0 +/* ETSI TS 101 503 V8.5.0 Seems to give Other def for this Messages??? */ + { 0xc0, "Utran Classmark Change" }, CONFLICTS WITH Handover To UTRAN Command + { 0xc1, "UE RAB Preconfiguration" }, + { 0xc2, "cdma2000 Classmark Change" }, +#endif + + /* ETSI TS 101 503 V8.5.0 */ + { 0x60, "Utran Classmark Change" }, + { 0x62, "cdma2000 Classmark Change" }, + { 0x63, "Inter System to UTRAN Handover Command" }, + { 0x64, "Inter System to cdma2000 Handover Command" }, + { 0x18, "System Information Type 8" }, + { 0x19, "System Information Type 1" }, + { 0x1a, "System Information Type 2" }, + { 0x1b, "System Information Type 3" }, + { 0x1c, "System Information Type 4" }, + { 0x1d, "System Information Type 5" }, + { 0x1e, "System Information Type 6" }, + { 0x1f, "System Information Type 7" }, + + { 0x02, "System Information Type 2bis" }, + { 0x03, "System Information Type 2ter" }, + { 0x07, "System Information Type 2quater" }, + { 0x05, "System Information Type 5bis" }, + { 0x06, "System Information Type 5ter" }, + { 0x04, "System Information Type 9" }, + { 0x00, "System Information Type 13" }, + + { 0x3d, "System Information Type 16" }, + { 0x3e, "System Information Type 17" }, + + { 0x40, "System Information Type 18" }, + { 0x41, "System Information Type 19" }, + { 0x42, "System Information Type 20" }, + + { 0x10, "Channel Mode Modify" }, + { 0x12, "RR Status" }, + { 0x17, "Channel Mode Modify Acknowledge" }, + { 0x14, "Frequency Redefinition" }, + { 0x15, "Measurement Report" }, + { 0x16, "Classmark Change" }, + { 0x13, "Classmark Enquiry" }, + { 0x36, "Extended Measurement Report" }, + { 0x37, "Extended Measurement Order" }, + { 0x34, "GPRS Suspension Request" }, + + { 0x09, "VGCS Uplink Grant" }, + { 0x0e, "Uplink Release" }, + { 0x0c, "Reserved" }, + { 0x2a, "Uplink Busy" }, + { 0x11, "Talker Indication" }, + + { 0xc0, "UTRAN Classmark Change/Handover To UTRAN Command" }, /* spec conflict */ + + { 0x38, "Application Information" }, + + { 0, NULL } +}; + +const value_string gsm_rr_elem_strings[] = { + /* Radio Resource Management Information Elements 10.5.2, most are from 10.5.1 */ + { 0x00, "BA Range" }, /* [3] 10.5.2.1a BA Range */ + { 0x00, "Cell Channel Description" }, /* [3] 10.5.2.1b */ + { 0x00, "BA List Pref" }, /* [3] 10.5.2.1c BA List Pref */ + { 0x00, "UTRAN Frequency List" }, /* [3] 10.5.2.1d UTRAN Frequency List */ + { 0x00, "Cell Selection Indicator after Release of all TCH and SDCCH" }, /* [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH IE */ + { 0x00, "Cell Description" }, /* 10.5.2.2 */ + { 0x00, "Cell Options (BCCH)" }, /* [3] 10.5.2.3 Cell Options (BCCH) */ + { 0x00, "Cell Options (SACCH)" }, /* [3] 10.5.2.3a Cell Options (SACCH) */ + { 0x00, "Cell Selection Parameters" }, /* [3] 10.5.2.4 Cell Selection Parameters */ +/* [3] 10.5.2.4a (void) */ + { 0x00, "Channel Description" }, /* 10.5.2.5 */ + { 0x00, "Channel Description 2" }, /* 10.5.2.5a */ + { 0x00, "Channel Description 3" }, /* 10.5.2.5c */ + { 0x00, "Channel Mode" }, /* [3] 10.5.2.6 */ + { 0x00, "Channel Mode 2" }, /* [3] 10.5.2.7 */ + { 0x00, "UTRAN Classmark" }, /* [3] 10.5.2.7a */ +/* [3] 10.5.2.7b (void) */ + { 0x00, "Classmark Enquiry Mask" }, /* [3] 10.5.2.7c */ +/* [3] 10.5.2.7d GERAN Iu Mode Classmark information element */ + { 0x00, "Channel Needed"}, /* [3] 10.5.2.8 */ + /* [3] 10.5.2.8a (void) */ + /* [3] 10.5.2.8b Channel Request Description 2 */ + /* Pos 20 */ + { 0x00, "Cipher Mode Setting" }, /* [3] 10.5.2.9 */ + { 0x00, "Cipher Mode Response" }, /* [3] 10.5.2.10 */ + { 0x00, "Control Channel Description" }, /* [3] 10.5.2.11 Control Channel Description */ +/* [3] 10.5.2.11a DTM Information Details */ + { 0x00, "Dynamic ARFCN Mapping" }, /* [3] 10.5.2.11b */ + { 0x00, "Frequency Channel Sequence" }, /* [3] 10.5.2.12 */ + { 0x00, "Frequency List" }, /* 10.5.2.13 */ + { 0x00, "Frequency Short List" }, /* 10.5.2.14 */ + { 0x00, "Frequency Short List2" }, /* 10.5.2.14a */ +/* [3] 10.5.2.14b Group Channel Description */ + { 0x00, "GPRS Resumption" }, /* [3] 10.5.2.14c GPRS Resumption */ + { 0x00, "GPRS Broadcast Information" }, /* [3] 10.5.2.14d GPRS broadcast information */ +/* [3] 10.5.2.14e Enhanced DTM CS Release Indication */ + { 0x00, "Handover Reference" }, /* 10.5.2.15 */ + { 0x00, "IA Rest Octets" }, /* [3] 10.5.2.16 */ + { 0x00, "IAR Rest Octets" }, /* [3] 10.5.2.17 IAR Rest Octets */ + { 0x00, "IAX Rest Octets" }, /* [3] 10.5.2.18 IAX Rest Octets */ + { 0x00, "L2 Pseudo Length" }, /* [3] 10.5.2.19 */ + { 0x00, "Measurement Results" }, /* [3] 10.5.2.20 Measurement Results */ +/* + * [3] 10.5.2.20a GPRS Measurement Results + */ + { 0x00, "Mobile Allocation" }, /* [3] 10.5.2.21 */ + { 0x00, "Mobile Time Difference" }, /* [3] 10.5.2.21a */ + { 0x00, "MultiRate configuration" }, /* [3] 10.5.2.21aa */ + /* Pos 30 */ + { 0x00, "Multislot Allocation" }, /* [3] 10.5.2.21b */ + /* + * [3] 10.5.2.21c NC mode + */ + { 0x00, "Neighbour Cell Description" }, /* [3] 10.5.2.22 Neighbour Cell Description */ + { 0x00, "Neighbour Cell Description 2" }, /* [3] 10.5.2.22a Neighbour Cell Description 2 */ +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets */ + { 0x00, "P1 Rest Octets" }, /* [3] 10.5.2.23 P1 Rest Octets */ + { 0x00, "P2 Rest Octets" }, /* [3] 10.5.2.24 P2 Rest Octets */ + { 0x00, "P3 Rest Octets" }, /* [3] 10.5.2.25 P3 Rest Octets */ + { 0x00, "Packet Channel Description" }, /* [3] 10.5.2.25a */ + { 0x00, "Dedicated mode or TBF" }, /* [3] 10.5.2.25b */ + /* [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment */ + { 0x00, "Page Mode" }, /* [3] 10.5.2.26 */ +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ + { 0x00, "NCC Permitted" }, /* [3] 10.5.2.27 NCC Permitted */ + { 0x00, "Power Command" }, /* 10.5.2.28 */ + { 0x00, "Power Command and access type" }, /* 10.5.2.28a */ + { 0x00, "RACH Control Parameters" }, /* [3] 10.5.2.29 RACH Control Parameters */ + { 0x00, "Request Reference" }, /* [3] 10.5.2.30 Request Reference */ + { 0x00, "RR Cause" }, /* 10.5.2.31 */ + { 0x00, "Synchronization Indication" }, /* 10.5.2.39 */ + { 0x00, "SI 1 Rest Octets" }, /* [3] 10.5.2.32 */ +/* [3] 10.5.2.33 SI 2bis Rest Octets */ + { 0x00, "SI 2ter Rest Octets" }, /* [3] 10.5.2.33a */ + { 0x00, "SI 2quater Rest Octets" }, /* [3] 10.5.2.33b */ + { 0x00, "SI 3 Rest Octets" }, /* [3] 10.5.2.34 */ + { 0x00, "SI 4 Rest Octets" }, /* [3] 10.5.2.35 */ + { 0x00, "SI 6 Rest Octets" }, /* [3] 10.5.2.35a */ +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + { 0x00, "SI 13 Rest Octets" }, /* [3] 10.5.2.37b */ +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets */ + { 0x00, "Starting Time" }, /* [3] 10.5.2.38 Starting Time */ + { 0x00, "Timing Advance" }, /* [3] 10.5.2.40 Timing Advance */ + { 0x00, "Time Difference" }, /* [3] 10.5.2.41 Time Difference */ + { 0x00, "TLLI" }, /* [3] 10.5.2.41a TLLI */ + { 0x00, "TMSI/P-TMSI" }, /* [3] 10.5.2.42 TMSI/P-TMSI */ + { 0x00, "VGCS target mode Indication" }, /* [3] 10.5.2.42a */ + /* Pos 40 */ + { 0x00, "VGCS Ciphering Parameters" }, /* [3] 10.5.2.42b */ + { 0x00, "Wait Indication" }, /* [3] 10.5.2.43 Wait Indication */ +/* [3] 10.5.2.44 SI10 rest octets $(ASCI)$ */ + { 0x00, "Extended Measurement Results" }, /* [3] 10.5.2.45 Extended Measurement Results */ + { 0x00, "Extended Measurement Frequency List" }, /* [3] 10.5.2.46 Extended Measurement Frequency List */ + { 0x00, "Suspension Cause" }, /* [3] 10.5.2.47 */ + { 0x00, "APDU ID" }, /* [3] 10.5.2.48 APDU ID */ + { 0x00, "APDU Flags" }, /* [3] 10.5.2.49 APDU Flags */ + { 0x00, "APDU Data" }, /* [3] 10.5.2.50 APDU Data */ + { 0x00, "Handover to UTRAN Command" }, /* [3] 10.5.2.51 Handover To UTRAN Command */ +/* [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell */ + { 0x00, "Service Support" }, /* [3] 10.5.2.57 */ + /* 10.5.2.58 MBMS p-t-m Channel Description */ + { 0x00, "Dedicated Service Information" }, /* [3] 10.5.2.59 */ +/* + * 10.5.2.60 MPRACH Description + * 10.5.2.61 Restriction Timer + * 10.5.2.62 MBMS Session Identity + * 10.5.2.63 Reduced group or broadcast call reference + * 10.5.2.64 Talker Priority status + * 10.5.2.65 Talker Identity + * 10.5.2.66 Token + * 10.5.2.67 PS Cause + * 10.5.2.68 VGCS AMR Configuration + */ + { 0x00, "Carrier Indication" }, /* 10.5.2.69 Carrier Indication */ + { 0, NULL } +}; + +const value_string gsm_rr_rest_octets_elem_strings[] = { + /* RR Rest Octets information elements */ + { 0, "UTRAN FDD Description" }, + { 0, "UTRAN TDD Description" }, + { 0, "3G Measurement Parameters Description" }, + { 0, "3G Additional Measurement Parameters Description" }, + { 0, "Measurement Parameters Description" }, + { 0, "GPRS Real Time Difference Description" }, + { 0, "GPRS BSIC Description" }, + { 0, "GPRS Report Priority Description" }, + { 0, "GPRS Measurement Parameters Description" }, + { 0, "NC Measurement Parameters" }, + { 0, "SI2q Extension Information" }, + { 0, "CCN Support Description" }, + { 0, "3G Neighbour Cell Description" }, + { 0, "FDD Cell Information Field" }, + { 0, "TDD Cell Information Field" }, + { 0, "GPRS 3G Measurement Parameters Description" }, + { 0, "3G Additional Measurement Parameters Description 2" }, + { 0, "Priority and E-UTRAN Parameters Description" }, + { 0, "Serving Cell Priority Parameters Description" }, + { 0, "3G Priority Parameters Description" }, + { 0, "UTRAN Priority Parameters" }, + { 0, "E-UTRAN Parameters Description" }, + { 0, "E-UTRAN Neighbour Cells" }, + { 0, "E-UTRAN Not Allowed Cells" }, + { 0, "E-UTRAN PCID to TA mapping" }, + { 0, "3G CSG Description" }, + { 0, "E-UTRAN CSG Description" }, + { 0, "Optional Selection Parameters" }, + { 0, "GPRS Indicator" }, + { 0, "SI4 Rest Octets_O" }, + { 0, "SI4 Rest Octets_S" }, + { 0, "LSA Parameters" }, + { 0, "LSA ID Information" }, + { 0, "PCH and NCH Info" }, + { 0, "VBS/VGCS Options" }, + { 0, "GPRS Mobile Allocation" }, + { 0, "GPRS Cell Options" }, + { 0, "GPRS Cell Options Extension Information" }, + { 0, "GPRS Power Control Parameters" }, + { 0, "PBCCH Description" }, + { 0, "GSM Description" }, + { 0, "Real Time Difference Description" }, + { 0, "BSIC Description" }, + { 0, "Report Priority Description" }, + { 0, "CDMA2000 Description" }, + { 0, "Serving cell data" }, + { 0, "Repeated Invalid BSIC Information" }, + { 0, "Bitmap Type Reporting" }, + { 0, "3G Supplementary Parameters Description" }, + { 0, "UTRAN Measurement Control Parameters" }, + { 0, "EGPRS Packet Uplink Assignment" }, + { 0, "Multiple Blocks Packet Downlink Assignment" }, + { 0, "Temporary Mobile Group Identity (TMGI)" }, + { 0, "Packet Timing Advance" }, + { 0, "Packet Uplink Assignment" }, + { 0, "Packet Downlink Assignment" }, + { 0, "Second Part Packet Assignment" }, + { 0, "REPORTING QUANTITY" }, + { 0, "E-UTRAN Measurement Report" }, + { 0, NULL } +}; + + +/* RR cause value (octet 2) TS 44.018 6.11.0*/ +static const value_string gsm_a_rr_RR_cause_vals[] = { + { 0, "Normal event"}, + { 1, "Abnormal release, unspecified"}, + { 2, "Abnormal release, channel unacceptable"}, + { 3, "Abnormal release, timer expired"}, + { 4, "Abnormal release, no activity on the radio path"}, + { 5, "Preemptive release"}, + { 6, "UTRAN configuration unknown"}, + { 8, "Handover impossible, timing advance out of range"}, + { 9, "Channel mode unacceptable"}, + { 10, "Frequency not implemented"}, + { 13, "Originator or talker leaving group call area"}, + { 12, "Lower layer failure"}, + { 0x41, "Call already cleared"}, + { 0x5f, "Semantically incorrect message"}, + { 0x60, "Invalid mandatory information"}, + { 0x61, "Message type non-existent or not implemented"}, + { 0x62, "Message type not compatible with protocol state"}, + { 0x64, "Conditional IE error"}, + { 0x65, "No cell allocation available"}, + { 0x6f, "Protocol error unspecified"}, + { 0, NULL } +}; + +static const value_string gsm_a_algorithm_identifier_vals[] = { + { 0, "Cipher with algorithm A5/1"}, + { 1, "Cipher with algorithm A5/2"}, + { 2, "Cipher with algorithm A5/3"}, + { 3, "Cipher with algorithm A5/4"}, + { 4, "Cipher with algorithm A5/5"}, + { 5, "Cipher with algorithm A5/6"}, + { 6, "Cipher with algorithm A5/7"}, + { 7, "Reserved"}, + { 0, NULL } +}; + + +#define DTAP_PD_MASK 0x0f +#define DTAP_SKIP_MASK 0xf0 +#define DTAP_TI_MASK DTAP_SKIP_MASK +#define DTAP_TIE_PRES_MASK 0x07 /* after TI shifted to right */ +#define DTAP_TIE_MASK 0x7f + +#define DTAP_RR_IEI_MASK 0xff + +/* Initialize the protocol and registered fields */ +static int proto_a_ccch = -1; +static int proto_a_sacch = -1; + +static int hf_gsm_a_dtap_msg_rr_type = -1; +int hf_gsm_a_rr_elem_id = -1; + +static int hf_gsm_a_rr_short_pd_msg_type = -1; +static int hf_gsm_a_rr_short_pd = -1; +static int hf_gsm_a_rr_short_l2_header = -1; + + +static int hf_gsm_a_bcc = -1; +static int hf_gsm_a_ncc = -1; +static int hf_gsm_a_bcch_arfcn = -1; +static int hf_gsm_a_rr_range_nb = -1; +static int hf_gsm_a_rr_range_lower = -1; +static int hf_gsm_a_rr_range_higher = -1; +static int hf_gsm_a_rr_ba_list_pref_length = -1; +static int hf_gsm_a_rr_ba_freq = -1; +static int hf_gsm_a_rr_utran_freq_list_length = -1; +static int hf_gsm_a_rr_ho_ref_val = -1; +static int hf_gsm_a_rr_L2_pseudo_len = -1; +static int hf_gsm_a_rr_ba_used = -1; +static int hf_gsm_a_rr_dtx_used = -1; +static int hf_gsm_a_rr_3g_ba_used = -1; +static int hf_gsm_a_rr_meas_valid = -1; +static int hf_gsm_a_rr_rxlev_full_serv_cell = -1; +static int hf_gsm_a_rr_rxlev_sub_serv_cell = -1; +static int hf_gsm_a_rr_rxqual_full_serv_cell = -1; +static int hf_gsm_a_rr_rxqual_sub_serv_cell = -1; +static int hf_gsm_a_rr_no_ncell_m = -1; +static int hf_gsm_a_rr_rxlev_ncell = -1; +static int hf_gsm_a_rr_bcch_freq_ncell = -1; +static int hf_gsm_a_rr_bsic_ncell = -1; +static int hf_gsm_a_rr_mobile_time_difference = -1; +static int hf_gsm_a_rr_pow_cmd_atc = -1; +static int hf_gsm_a_rr_pow_cmd_epc = -1; +static int hf_gsm_a_rr_page_mode = -1; +static int hf_gsm_a_rr_dedicated_mode_or_tbf = -1; +static int hf_gsm_a_rr_pow_cmd_fpcepc = -1; +static int hf_gsm_a_rr_pow_cmd_powlev = -1; +static int hf_gsm_a_rr_sync_ind_nci = -1; +static int hf_gsm_a_rr_sync_ind_rot = -1; +static int hf_gsm_a_rr_sync_ind_si = -1; +static int hf_gsm_a_rr_format_id = -1; +static int hf_gsm_a_rr_format_id2 = -1; +static int hf_gsm_a_rr_channel_mode = -1; +static int hf_gsm_a_rr_channel_mode2 = -1; +static int hf_gsm_a_rr_sc = -1; +static int hf_gsm_a_algorithm_id = -1; +static int hf_gsm_a_rr_cr = -1; +static int hf_gsm_a_rr_multirate_speech_ver = -1; +static int hf_gsm_a_rr_NCSB = -1; +static int hf_gsm_a_rr_ICMI = -1; +static int hf_gsm_a_rr_start_mode = -1; +static int hf_gsm_a_rr_timing_adv = -1; +static int hf_gsm_a_rr_time_diff = -1; +static int hf_gsm_a_rr_tlli = -1; +static int hf_gsm_a_rr_tmsi_ptmsi = -1; +static int hf_gsm_a_rr_target_mode = -1; +static int hf_gsm_a_rr_wait_indication = -1; +static int hf_gsm_a_rr_seq_code = -1; +static int hf_gsm_a_rr_group_cipher_key_number = -1; +static int hf_gsm_a_rr_MBMS_multicast = -1; +static int hf_gsm_a_rr_MBMS_broadcast = -1; +static int hf_gsm_a_rr_last_segment = -1; +static int hf_gsm_a_rr_carrier_ind = -1; +static int hf_gsm_a_rr_ra = -1; +static int hf_gsm_a_rr_T1prim = -1; +static int hf_gsm_a_rr_T3 = -1; +static int hf_gsm_a_rr_T2 = -1; +static int hf_gsm_a_rr_rfn = -1; +static int hf_gsm_a_rr_RR_cause = -1; +static int hf_gsm_a_rr_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_utran_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_cdma200_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_geran_iu_cm_cng_msg_req = -1; +int hf_gsm_a_rr_chnl_needed_ch1 = -1; +static int hf_gsm_a_rr_chnl_needed_ch2 = -1; +static int hf_gsm_a_rr_chnl_needed_ch3 = -1; +static int hf_gsm_a_rr_chnl_needed_ch4 = -1; +static int hf_gsm_a_rr_suspension_cause = -1; +static int hf_gsm_a_rr_apdu_id = -1; +static int hf_gsm_a_rr_apdu_flags = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1 = -1; +static int hf_gsm_a_rr_amr_threshold = -1; +static int hf_gsm_a_rr_amr_hysteresis = -1; +static int hf_gsm_a_rr_pwrc = -1; +static int hf_gsm_a_rr_dtx_bcch = -1; +static int hf_gsm_a_rr_dtx_sacch = -1; +static int hf_gsm_a_rr_radio_link_timeout = -1; +static int hf_gsm_a_rr_cell_reselect_hyst = -1; +static int hf_gsm_a_rr_ms_txpwr_max_cch = -1; +static int hf_gsm_a_rr_acs = -1; +static int hf_gsm_a_rr_neci = -1; +static int hf_gsm_a_rr_rxlev_access_min = -1; +static int hf_gsm_a_rr_mscr = -1; +static int hf_gsm_a_rr_att = -1; +static int hf_gsm_a_rr_ccch_conf = -1; +static int hf_gsm_a_rr_cbq3 = -1; +static int hf_gsm_a_rr_bs_pa_mfrms = -1; +static int hf_gsm_a_rr_bs_ag_blks_res = -1; +static int hf_gsm_a_rr_t3212 = -1; +static int hf_gsm_a_rr_dyn_arfcn_length = -1; +static int hf_gsm_a_rr_gsm_band = -1; +static int hf_gsm_a_rr_arfcn_first = -1; +static int hf_gsm_a_rr_band_offset = -1; +static int hf_gsm_a_rr_arfcn_range = -1; +static int hf_gsm_a_rr_lowest_arfcn = -1; +static int hf_gsm_a_rr_inc_skip_arfcn = -1; +static int hf_gsm_a_rr_gprs_resumption_ack = -1; +static int hf_gsm_a_rr_ext_ind = -1; +static int hf_gsm_a_rr_ba_ind = -1; +static int hf_gsm_a_rr_multiband_reporting = -1; +static int hf_gsm_a_rr_ncc_permitted = -1; +static int hf_gsm_a_rr_max_retrans = -1; +static int hf_gsm_a_rr_tx_integer = -1; +static int hf_gsm_a_rr_cell_barr_access = -1; +static int hf_gsm_a_rr_re = -1; +static int hf_gsm_a_rr_acc = -1; +static int hf_gsm_a_rr_nch_position = -1; +static int hf_gsm_a_rr_si2ter_mp_change_mark = -1; +static int hf_gsm_a_rr_si2ter_3g_change_mark = -1; +static int hf_gsm_a_rr_si2ter_index = -1; +static int hf_gsm_a_rr_si2ter_count = -1; +static int hf_gsm_a_rr_fdd_uarfcn = -1; +static int hf_gsm_a_rr_bandwidth_fdd = -1; +static int hf_gsm_a_rr_tdd_uarfcn = -1; +static int hf_gsm_a_rr_bandwidth_tdd = -1; +static int hf_gsm_a_rr_arfcn = -1; +static int hf_gsm_a_rr_bsic = -1; +static int hf_gsm_a_rr_qsearch_i = -1; +static int hf_gsm_a_rr_fdd_qoffset = -1; +static int hf_gsm_a_rr_fdd_qmin = -1; +static int hf_gsm_a_rr_tdd_qoffset = -1; +static int hf_gsm_a_rr_fdd_qmin_offset = -1; +static int hf_gsm_a_rr_fdd_rscpmin = -1; +static int hf_gsm_a_rr_3g_ba_ind = -1; +static int hf_gsm_a_rr_mp_change_mark = -1; +static int hf_gsm_a_rr_si2quater_index = -1; +static int hf_gsm_a_rr_si2quater_count = -1; +static int hf_gsm_a_rr_gsm_report_type = -1; +static int hf_gsm_a_rr_serving_band_reporting = -1; +static int hf_gsm_a_rr_frequency_scrolling = -1; +static int hf_gsm_a_rr_rep_priority = -1; +static int hf_gsm_a_rr_report_type = -1; +static int hf_gsm_a_rr_reporting_rate = -1; +static int hf_gsm_a_rr_invalid_bsic_reporting = -1; +static int hf_gsm_a_rr_scale_ord = -1; +static int hf_gsm_a_rr_900_reporting_offset = -1; +static int hf_gsm_a_rr_900_reporting_threshold = -1; +static int hf_gsm_a_rr_1800_reporting_offset = -1; +static int hf_gsm_a_rr_1800_reporting_threshold = -1; +static int hf_gsm_a_rr_400_reporting_offset = -1; +static int hf_gsm_a_rr_400_reporting_threshold = -1; +static int hf_gsm_a_rr_1900_reporting_offset = -1; +static int hf_gsm_a_rr_1900_reporting_threshold = -1; +static int hf_gsm_a_rr_850_reporting_offset = -1; +static int hf_gsm_a_rr_850_reporting_threshold = -1; +static int hf_gsm_a_rr_network_control_order = -1; +static int hf_gsm_a_rr_nc_non_drx_period = -1; +static int hf_gsm_a_rr_nc_reporting_period_i = -1; +static int hf_gsm_a_rr_nc_reporting_period_t = -1; +static int hf_gsm_a_rr_index_start_3g = -1; +static int hf_gsm_a_rr_absolute_index_start_emr = -1; +static int hf_gsm_a_rr_qsearch_c_initial = -1; +static int hf_gsm_a_rr_fdd_rep_quant = -1; +static int hf_gsm_a_rr_fdd_multirat_reporting = -1; +static int hf_gsm_a_rr_tdd_multirat_reporting = -1; +static int hf_gsm_a_rr_qsearch_p = -1; +static int hf_gsm_a_rr_3g_search_prio = -1; +static int hf_gsm_a_rr_fdd_reporting_offset = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_rscp = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_ecn0 = -1; +static int hf_gsm_a_rr_tdd_reporting_offset = -1; +static int hf_gsm_a_rr_tdd_reporting_threshold_rscp = -1; +static int hf_gsm_a_rr_tdd_reporting_threshold_ecn0 = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_3g_ccn_active = -1; +static int hf_gsm_a_rr_700_reporting_offset = -1; +static int hf_gsm_a_rr_700_reporting_threshold = -1; +static int hf_gsm_a_rr_810_reporting_offset = -1; +static int hf_gsm_a_rr_810_reporting_threshold = -1; +static int hf_gsm_a_rr_cbq = -1; +static int hf_gsm_a_rr_cell_reselect_offset = -1; +static int hf_gsm_a_rr_temporary_offset = -1; +static int hf_gsm_a_rr_penalty_time = -1; +static int hf_gsm_a_rr_gprs_ra_colour = -1; +static int hf_gsm_a_rr_si13_position = -1; +static int hf_gsm_a_rr_power_offset = -1; +static int hf_gsm_a_rr_si2quater_position = -1; +static int hf_gsm_a_rr_si13alt_position = -1; +static int hf_gsm_a_rr_prio_thr = -1; +static int hf_gsm_a_rr_lsa_offset = -1; +static int hf_gsm_a_rr_paging_channel_restructuring = -1; +static int hf_gsm_a_rr_nln_sacch = -1; +static int hf_gsm_a_rr_nln_status_sacch = -1; +static int hf_gsm_a_rr_nln_pch = -1; +static int hf_gsm_a_rr_nln_status_pch = -1; +static int hf_gsm_a_rr_vbs_vgcs_inband_notifications = -1; +static int hf_gsm_a_rr_vbs_vgcs_inband_pagings = -1; +static int hf_gsm_a_rr_rac = -1; +static int hf_gsm_a_rr_max_lapdm = -1; +static int hf_gsm_a_rr_gprs_ms_txpwr_max_ccch = -1; +static int hf_gsm_a_rr_dedicated_mode_mbms_notification_support = -1; +static int hf_gsm_a_rr_mnci_support = -1; +static int hf_gsm_a_rr_amr_config = -1; +static int hf_gsm_a_rr_bcch_change_mark = -1; +static int hf_gsm_a_rr_si_change_field = -1; +static int hf_gsm_a_rr_si13_change_mark = -1; +static int hf_gsm_a_rr_hsn = -1; +static int hf_gsm_a_rr_rfl_number = -1; +static int hf_gsm_a_rr_arfcn_index = -1; +static int hf_gsm_a_rr_ma_length = -1; +static int hf_gsm_a_rr_psi1_repeat_period = -1; +static int hf_gsm_a_rr_pbcch_pb = -1; +static int hf_gsm_a_rr_pbcch_tsc = -1; +static int hf_gsm_a_rr_pbcch_tn = -1; +static int hf_gsm_a_rr_spgc_ccch_sup = -1; +static int hf_gsm_a_rr_priority_access_thr = -1; +static int hf_gsm_a_rr_nmo = -1; +static int hf_gsm_a_rr_t3168 = -1; +static int hf_gsm_a_rr_t3192 = -1; +static int hf_gsm_a_rr_drx_timer_max = -1; +static int hf_gsm_a_rr_access_burst_type = -1; +static int hf_gsm_a_rr_control_ack_type = -1; +static int hf_gsm_a_rr_bs_cv_max = -1; +static int hf_gsm_a_rr_pan_dec = -1; +static int hf_gsm_a_rr_pan_inc = -1; +static int hf_gsm_a_rr_pan_max = -1; +static int hf_gsm_a_rr_egprs_packet_channel_request = -1; +static int hf_gsm_a_rr_bep_period = -1; +static int hf_gsm_a_rr_pfc_feature_mode = -1; +static int hf_gsm_a_rr_dtm_support = -1; +static int hf_gsm_a_rr_bss_paging_coordination = -1; +static int hf_gsm_a_rr_ccn_active = -1; +static int hf_gsm_a_rr_nw_ext_utbf = -1; +static int hf_gsm_a_rr_multiple_tbf_capability = -1; +static int hf_gsm_a_rr_ext_utbf_no_data = -1; +static int hf_gsm_a_rr_dtm_enhancements_capability = -1; +static int hf_gsm_a_rr_reduced_latency_access = -1; +static int hf_gsm_a_rr_alpha = -1; +static int hf_gsm_a_rr_t_avg_w = -1; +static int hf_gsm_a_rr_t_avg_t = -1; +static int hf_gsm_a_rr_pc_meas_chan = -1; +static int hf_gsm_a_rr_n_avg_i = -1; +static int hf_gsm_a_rr_sgsnr = -1; +static int hf_gsm_a_rr_si_status_ind = -1; +static int hf_gsm_a_rr_lb_ms_txpwr_max_cch = -1; +static int hf_gsm_a_rr_si2n_support = -1; +static int hf_gsm_a_rr_mi_index = -1; +static int hf_gsm_a_rr_mi_count = -1; +static int hf_gsm_a_rr_3g_wait = -1; +static int hf_gsm_a_rr_qsearch_c = -1; +static int hf_gsm_a_rr_bsic_seen = -1; +static int hf_gsm_a_rr_scale = -1; +static int hf_gsm_a_rr_mean_bep_gmsk = -1; +static int hf_gsm_a_rr_mean_cv_bep = -1; +static int hf_gsm_a_rr_nbr_rcvd_blocks = -1; +static int hf_gsm_a_rr_reporting_quantity = -1; +static int hf_gsm_a_rr_extended_ra = -1; +static int hf_gsm_a_rr_access_tech_type = -1; +static int hf_gsm_a_rr_tfi_assignment = -1; +static int hf_gsm_a_rr_polling = -1; +static int hf_gsm_a_rr_usf = -1; +static int hf_gsm_a_rr_usf_granularity = -1; +static int hf_gsm_a_rr_p0 = -1; +static int hf_gsm_a_rr_pr_mode = -1; +static int hf_gsm_a_rr_egprs_mcs = -1; +static int hf_gsm_a_rr_tlli_block_channel_coding = -1; +static int hf_gsm_a_rr_bep_period2 = -1; +static int hf_gsm_a_rr_resegment = -1; +static int hf_gsm_a_rr_egprs_window_size = -1; +static int hf_gsm_a_rr_gamma = -1; +static int hf_gsm_a_rr_timing_adv_index = -1; +static int hf_gsm_a_rr_timing_adv_timeslot_num = -1; +static int hf_gsm_a_rr_tbf_starting_time = -1; +static int hf_gsm_a_rr_num_of_radio_block_allocated = -1; +static int hf_gsm_a_rr_pfi = -1; +static int hf_gsm_a_rr_mbms_service_id = -1; +static int hf_gsm_a_rr_ms_id = -1; +static int hf_gsm_a_rr_gprs_cs = -1; +static int hf_gsm_a_rr_rlc_mode = -1; +static int hf_gsm_a_rr_ta_valid = -1; +static int hf_gsm_a_rr_link_quality_meas_mode = -1; +static int hf_gsm_a_rr_emr_bitmap_length = -1; +static int hf_gsm_a_rr_eutran_mr_n_eutran = -1; +static int hf_gsm_a_rr_eutran_mr_freq_idx = -1; +static int hf_gsm_a_rr_eutran_mr_cell_id = -1; +static int hf_gsm_a_rr_eutran_mr_rpt_quantity = -1; +static int hf_gsm_a_rr_ma_channel_set = -1; +static int hf_n_range_orig_arfcn = -1; + + + +/* Additions in Rel-8 */ +static int hf_gsm_a_rr_3g_priority_param_desc_utran_start = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_utran_stop = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin = -1; +static int hf_gsm_a_rr_utran_frequency_index = -1; +static int hf_gsm_a_rr_utran_priority = -1; +static int hf_gsm_a_rr_thresh_utran_high = -1; +static int hf_gsm_a_rr_thresh_utran_low = -1; +static int hf_gsm_a_rr_utran_qrxlevmin = -1; +static int hf_gsm_a_rr_eutran_ccn_active = -1; +static int hf_gsm_a_rr_eutran_start = -1; +static int hf_gsm_a_rr_eutran_stop = -1; +static int hf_gsm_a_rr_qsearch_c_eutran_initial = -1; +static int hf_gsm_a_rr_eutran_rep_quant = -1; +static int hf_gsm_a_rr_eutran_multirat_reporting = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_offset = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_offset = -1; +static int hf_gsm_a_rr_eutran_fdd_measurement_report_offset = -1; +static int hf_gsm_a_rr_eutran_tdd_measurement_report_offset = -1; +static int hf_gsm_a_rr_reporting_granularity = -1; +static int hf_gsm_a_rr_eutran_default_measurement_control_eutran = -1; +static int hf_gsm_a_rr_eutran_measurement_control_eutran = -1; +static int hf_gsm_a_rr_qsearch_p_eutran = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_geran_priority = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_h_prio = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_t_reselection = -1; +static int hf_gsm_a_rr_eutran_earfcn = -1; +static int hf_gsm_a_rr_eutran_measurement_bandwidth = -1; +static int hf_gsm_a_rr_eutran_priority = -1; +static int hf_gsm_a_rr_thresh_eutran_high = -1; +static int hf_gsm_a_rr_thresh_eutran_low = -1; +static int hf_gsm_a_rr_eutran_qrxlevmin = -1; +static int hf_gsm_a_rr_eutran_pcid = -1; +static int hf_gsm_a_rr_eutran_pcid_bitmap_group = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern_length = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern_sense = -1; +static int hf_gsm_a_rr_eutran_frequency_index = -1; +static int hf_gsm_a_rr_psc = -1; +static int hf_gsm_a_rr_utran_psc_pattern_length = -1; +static int hf_gsm_a_rr_utran_psc_pattern_sense = -1; +static int hf_gsm_a_rr_utran_csg_fdd_uarfcn = -1; +static int hf_gsm_a_rr_utran_csg_tdd_uarfcn = -1; +static int hf_gsm_a_rr_csg_earfcn = -1; +static int hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran = -1; + +/* Initialize the subtree pointers */ +static gint ett_ccch_msg = -1; +static gint ett_ccch_oct_1 = -1; +static gint ett_sacch_msg = -1; + +static char a_bigbuf[1024]; + +static dissector_handle_t data_handle; +static dissector_handle_t rrlp_dissector; + + +#define NUM_GSM_RR_ELEM (sizeof(gsm_rr_elem_strings)/sizeof(value_string)) +gint ett_gsm_rr_elem[NUM_GSM_RR_ELEM]; + +typedef enum +{ + /* RR Rest Octets information elements */ + DE_RR_REST_OCTETS_UTRAN_FDD_DESC, + DE_RR_REST_OCTETS_UTRAN_TDD_DESC, + DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_GPRS_RTD_DESC, + DE_RR_REST_OCTETS_GPRS_BSIC_DESC, + DE_RR_REST_OCTETS_GPRS_REPORT_PRIO_DESC, + DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_NC_MEAS_PARAM, + DE_RR_REST_OCTETS_SI2Q_EXT_INFO, + DE_RR_REST_OCTETS_CCN_SUPPORT_DESC, + DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC, + DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD, + DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD, + DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2, + DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC, + DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC, + DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC, + DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM, + DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC, + DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS, + DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS, + DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING, + DE_RR_REST_OCTETS_3G_CSG_DESC, + DE_RR_REST_OCTETS_EUTRAN_CSG_DESC, + DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM, + DE_RR_REST_OCTETS_GPRS_INDICATOR, + DE_RR_REST_OCTETS_SI4_REST_OCTETS_O, + DE_RR_REST_OCTETS_SI4_REST_OCTETS_S, + DE_RR_REST_OCTETS_LSA_PARAMETERS, + DE_RR_REST_OCTETS_LSA_ID_INFO, + DE_RR_REST_OCTETS_PCH_AND_NCH_INFO, + DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS, + DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC, + DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS, + DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO, + DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS, + DE_RR_REST_OCTETS_PBCCH_DESC, + DE_RR_REST_OCTETS_GSM_DESC, + DE_RR_REST_OCTETS_RTD_DESC, + DE_RR_REST_OCTETS_BSIC_DESC, + DE_RR_REST_OCTETS_REPORT_PRIO_DESC, + DE_RR_REST_OCTETS_CDMA2000_DESC, + DE_RR_REST_OCTETS_SERVING_CELL_DATA, + DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO, + DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING, + DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC, + DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC, + DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_TMGI, + DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE, + DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT, + DE_RR_REST_OCTETS_REPORTING_QUANTITY, + DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT, + DE_RR_REST_OCTETS_NONE +} +rr_rest_octets_elem_idx_t; + +#define NUM_GSM_RR_REST_OCTETS_ELEM (sizeof(gsm_rr_rest_octets_elem_strings)/sizeof(value_string)) +gint ett_gsm_rr_rest_octets_elem[NUM_GSM_RR_REST_OCTETS_ELEM]; + +/* + * Generates a string representing the bits in a bitfield at "bit_offset" from an 8 bit boundary + * with the length in bits of no_of_bits based on value. + * Ex: ..xx x... + */ + +/* this function is used for dissecting the 0/1 presence flags in CSN.1 coded IEs */ +static gboolean gsm_rr_csn_flag(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, const char *description, const char *true_string, const char * false_string) +{ + guint8 bit_mask = 0x80 >> (bit_offset % 8); + guint8 value = tvb_get_guint8(tvb, bit_offset >> 3); + char bits_str[] = {".... ...."}; + guint8 offset_in_octet = bit_offset % 8; + + if (value & bit_mask) + { + bits_str[offset_in_octet + (offset_in_octet / 4)] = '1'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, true_string); + return TRUE; + } + bits_str[offset_in_octet + (offset_in_octet / 4)] = '0'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, false_string); + return FALSE; +} + +/* this function is used for dissecting the H/L presence flags in CSN.1 coded IEs" + If truncation ( 44.018 section 8.9) is allowed, truncation_length is set to the actual length of the PDU, + otherwise it is set to 0 */ +static gboolean gsm_rr_csn_HL_flag(tvbuff_t *tvb, proto_tree *tree, guint8 truncation_length, gint bit_offset, const char *description, const char *true_string, const char * false_string) +{ + guint8 bit_mask = 0x80 >> (bit_offset % 8); + guint8 value = 0; + char bits_str[] = {".... ...."}; + guint8 offset_in_octet = bit_offset % 8; + + /* if truncation_length == 0, then don't check for truncation*/ + if ((!truncation_length)||((bit_offset>>3) < truncation_length)) + { + value = tvb_get_guint8(tvb, bit_offset >> 3)^PADDING_BYTE; + } + if (value & bit_mask) + { + bits_str[offset_in_octet + (offset_in_octet / 4)] = 'H'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, true_string); + return TRUE; + } + bits_str[offset_in_octet + (offset_in_octet / 4)] = 'L'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, false_string); + return FALSE; +} + +/* +10.5.2 Radio Resource management information elements + * [3] 10.5.2.1a BA Range + */ +static guint16 +de_rr_ba_range(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint8 value; + + curr_offset = offset; + proto_tree_add_item(tree, hf_gsm_a_rr_range_nb, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + value = tvb_get_guint8(tvb, curr_offset); + curr_offset += 1; + bit_offset = curr_offset << 3; + while (value) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_lower, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_higher, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + value -= 1; + } + + curr_offset += len - 1; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1b Cell Channel Description + */ + +#define ARFCN_MAX 1024 /* total number of ARFCNs defined */ + +static void display_channel_list(guint8 *list, tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len) +{ + int arfcn; + proto_item *ti=NULL; + + ti = proto_tree_add_text(tree, tvb, offset, len, "List of ARFCNs ="); + for (arfcn=0; arfcn> 3; + guint8 len = tvb_length(tvb); + + if (len > octet_offset) + { + /* there is spare room, check the first octet */ + guint8 bit_mask = 0xFF >> (bit_offset & 0x07); + if ((tvb_get_guint8(tvb, octet_offset) & bit_mask) != (PADDING_BYTE & bit_mask)) + found = TRUE; + + for (i=octet_offset+1; (i>= 1; + return j; +} + +static gint f_k(gint k, gint *w, gint range) +{ + gint idx, n, j; + + idx = k; + range -= 1; + range = range/greatest_power_of_2_lesser_or_equal_to(idx); + n = w[idx]-1; + + while (idx>1) { + j = greatest_power_of_2_lesser_or_equal_to(idx); + range = 2*range+1; + if ((2*idx) < 3*j){ /* left child */ + idx -= j/2; + n = (n+w[idx]-1+((range-1)/2)+1)%range; + } + else { /* right child */ + idx -= j; + n = (n+w[idx]-1+1)%range; + } + } + + return (n+1)%1024; +} + +static void dissect_channel_list_n_range(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gint range) +{ + gint curr_offset=offset, bit_offset, f0, arfcn_orig, w[64], wsize, i; + gint octet, nwi=1, jwi=0, imax, iused, arfcn; + guint8 list[1024]; + proto_item *item; + proto_tree *subtree; + + memset((void*)list,0,sizeof(list)); + + item = proto_tree_add_text(tree,tvb, curr_offset, len, "Range %d format", range); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_NEIGH_CELL_DESC]); + + octet = tvb_get_guint8(tvb, curr_offset); + if (range==1024) { + f0 = (octet>>2)&1; + if (f0) + list[0] = 1; + arfcn_orig = 0; + wsize = 10; + imax = 16; + bit_offset = curr_offset*8 + 6; + } + else { + bit_offset = curr_offset*8 + 7; + arfcn_orig = (gint) tvb_get_bits(tvb, bit_offset, 10, FALSE); + proto_tree_add_bits_item(subtree, hf_n_range_orig_arfcn, tvb, bit_offset, 10, FALSE); + bit_offset+=10; + + list[arfcn_orig] = 1; + + switch (range) { + case 512: + wsize=9; + imax = 17; + break; + case 256: + wsize=8; + imax = 21; + break; + case 128: + wsize=7; + imax = 28; + break; + default: + wsize=0; + imax = 0; + DISSECTOR_ASSERT_NOT_REACHED(); + } + } + iused = imax; /* in case the list is actually full */ + + /* extract the variable size w[] elements */ + for (i=1; i<=imax; i++) { + w[i] = (gint) tvb_get_bits(tvb, bit_offset, wsize, FALSE); + proto_tree_add_text(subtree, tvb, bit_offset>>3, ((bit_offset+wsize-1)>>3) - (bit_offset>>3) + 1 , "%s %s(%d): %d", + decode_bits_in_field(bit_offset, wsize, w[i]), + "W", + i, + w[i]); + bit_offset += wsize; + curr_offset = bit_offset>>3; + + if ((iused == imax) && (w[i] == 0) ) { + iused = i - 1; + } + if ((curr_offset-offset)>len) { + iused = i - 1; + break; + } + if (++jwi==nwi) { /* check if the number of wi at this wsize has been extracted */ + jwi = 0; /* reset the count of wi at this size */ + nwi <<= 1; /* get twice as many of the next size */ + wsize--; /* make the next size 1 bit smaller */ + } + } + + for (i=1; i<=iused; i++) { + arfcn = (f_k(i, w, range) + arfcn_orig)%1024; + list[arfcn] = 1; + } + + display_channel_list(list, tvb, tree, offset, curr_offset-offset); + + return; +} + +static guint16 +dissect_arfcn_list_core(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_, guint8 format) +{ + guint32 curr_offset,byte; + guint8 oct,bit; + guint16 arfcn; + proto_item *item; + + curr_offset = offset; + + if ((format & 0xc0) == 0x00) + { + /* bit map 0 */ + item = proto_tree_add_text(tree,tvb, curr_offset, len, "List of ARFCNs ="); + bit = 4; + arfcn = 125; + for (byte = 0;byte <= len-1;byte++) + { + oct = tvb_get_guint8(tvb, curr_offset); + while (bit-- != 0) + { + arfcn--; + if (((oct >> bit) & 1) == 1) + { + proto_item_append_text(item," %d",arfcn); + } + } + bit = 8; + curr_offset++; + } + } + else if ((format & 0xc8) == 0x80) + { + /* 1024 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 1024); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x88) + { + /* 512 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 512); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8a) + { + /* 256 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 256); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8c) + { + /* 128 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 128); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8e) + { + /* variable bit map */ + arfcn = ((format & 0x01) << 9) | (tvb_get_guint8(tvb, curr_offset+1) << 1) | ((tvb_get_guint8(tvb, curr_offset + 2) & 0x80) >> 7); + item = proto_tree_add_text(tree,tvb,curr_offset,len,"List of ARFCNs = %d",arfcn); + curr_offset = curr_offset + 2; + bit = 7; + for (byte = 0;byte <= len-3;byte++) + { + oct = tvb_get_guint8(tvb, curr_offset); + while (bit-- != 0) + { + arfcn++; + if (((oct >> bit) & 1) == 1) + { + proto_item_append_text(item," %d",arfcn); + } + } + bit = 8; + curr_offset++; + } + } + + return(curr_offset - offset); +} + +/* + * Format ID is in bits: + * 128 127 124 123 122 (hf_gsm_a_rr_format_id) + */ +static guint16 +dissect_arfcn_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + + curr_offset = offset; + + oct = tvb_get_guint8(tvb, curr_offset); + + /* FORMAT-ID, Format Identifier (part of octet 3)*/ + proto_tree_add_item(tree, hf_gsm_a_rr_format_id, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += dissect_arfcn_list_core(tvb, tree, pinfo, offset, len, add_string, string_len, oct); + + return(curr_offset - offset); +} + +/* + * Format ID is in bits: + * 128 124 123 122 (hf_gsm_a_rr_format_id2) + */ +static guint16 +dissect_arfcn_list2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + + curr_offset = offset; + + /* Turn bit 127 off, in order to reuse the ARFCN dissection code */ + oct = tvb_get_guint8(tvb, curr_offset) & 0xbf; + + /* FORMAT-ID, Format Identifier (part of octet 3)*/ + proto_tree_add_item(tree, hf_gsm_a_rr_format_id2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += dissect_arfcn_list_core(tvb, tree, pinfo, offset, len, add_string, string_len, oct); + + return(curr_offset - offset); +} + +static guint16 +de_rr_cell_ch_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} +/* + * [3] 10.5.2.1c BA List Pref + */ +static guint16 +de_rr_ba_list_pref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + bit_offset = curr_offset << 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_list_pref_length, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating Range Limits", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_lower, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_higher, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating BA Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_freq, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1d UTRAN Frequency List + */ +static guint16 +de_rr_utran_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + /* < UTRAN Freq List >::= + * < LENGTH OF UTRAN FREQ LIST : bit (8) > -- length following in octets + * { 1 < FDD_ARFCN > : bit (14) } ** 0 -- FDD frequencies + * { 1 < TDD_ARFCN > : bit (14) } ** 0 -- TDD frequencies + * **; + * Spare bits in the end of the field are used to fill the last octet. + */ + bit_offset = curr_offset << 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_utran_freq_list_length, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating FDD Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating TDD Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH + */ +static const guint8 +convert_n_to_p[32] = { 0, 10, 19, 28, 36, 44, 52, 60, 67, 74, 81, 88, 95, 102, 109, 116, + 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const guint8 +convert_n_to_q[32] = { 0, 9, 17, 25, 32, 39, 46, 53, 59, 65, 71, 77, 83, 89, 95, 101, + 106, 111, 116, 121, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static guint16 +de_rr_cell_select_indic(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, idx, xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + guint8 value; + + curr_offset = offset; + bit_offset = curr_offset << 3; + value = tvb_get_bits8(tvb,bit_offset,3); + bit_offset += 3; + switch (value) + { + case 0: /* GSM Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GSM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GSM_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating GSM Description struct", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Band Indicator", "1900", "1800"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_arfcn, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + case 1: /* UTRAN FDD Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating UTRAN FDD Description struct", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_fdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Cell Information", "Present", "Not Present")) + { + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Indic0", "1", "0"); + idx = tvb_get_bits8(tvb,bit_offset,5); + proto_tree_add_text(subtree,tvb, bit_offset>>3, 1, "Nr of FDD Cells : %d", idx); + bit_offset += 5; + idx = convert_n_to_p[idx]; + item2 = proto_tree_add_text(subtree,tvb, bit_offset>>3, (idx>>3)+1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Scrambling Code: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Diversity: %d", 0); + } + if (idx) + { + wsize = 10; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, bit_offset, wsize, ENC_BIG_ENDIAN); + bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 1024); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Scrambling Code: %d", xdd_cell_info & 0x01FF); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Diversity: %d", (xdd_cell_info >> 9) & 0x01); + } + } + } + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + case 2: /* UTRAN TDD Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating UTRAN TDD Description struct", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_tdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Cell Information", "Present", "Not Present")) + { + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Indic0", "1", "0"); + idx = tvb_get_bits8(tvb,bit_offset,5); + proto_tree_add_text(subtree,tvb, bit_offset>>3, 1, "Nr of TDD Cells : %d", idx); + bit_offset += 5; + idx = convert_n_to_q[idx]; + item2 = proto_tree_add_text(subtree,tvb, bit_offset>>3, (idx>>3)+1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Cell Parameter: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Sync Case TSTD: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Diversity TDD: %d", 0); + } + if (idx) + { + wsize = 9; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, bit_offset, wsize, ENC_BIG_ENDIAN); + bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 512); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Cell Parameter: %d", xdd_cell_info & 0x07F); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Sync Case TSTD: %d", (xdd_cell_info >> 7) & 0x01); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Diversity TDD: %d", (xdd_cell_info >> 8) & 0x01); + } + } + } + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + default: + break; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.2 Cell Description + */ +guint16 +de_rr_cell_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + guint16 bcch_arfcn; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 2, "%s", + gsm_rr_elem_strings[DE_RR_CELL_DSC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_DSC]); + + proto_tree_add_item(subtree, hf_gsm_a_ncc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_bcc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + bcch_arfcn = (tvb_get_guint8(tvb,curr_offset) & 0xc0) << 2; + bcch_arfcn = bcch_arfcn | tvb_get_guint8(tvb,curr_offset+1); + proto_tree_add_uint(subtree, hf_gsm_a_bcch_arfcn , tvb, curr_offset, 2, bcch_arfcn ); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.3 Cell Options (BCCH) + */ +static const value_string gsm_a_rr_dtx_bcch_vals[] = { + { 0x00, "The MSs may use uplink discontinuous transmission" }, + { 0x01, "The MSs shall use uplink discontinuous transmission" }, + { 0x02, "The MSs shall not use uplink discontinuous transmission" }, + { 0x03, "Reserved" }, + { 0, NULL } }; + +static const value_string gsm_a_rr_radio_link_timeout_vals[] = { + { 0x00, "4" }, + { 0x01, "8" }, + { 0x02, "12" }, + { 0x03, "16" }, + { 0x04, "20" }, + { 0x05, "24" }, + { 0x06, "28" }, + { 0x07, "32" }, + { 0x08, "36" }, + { 0x09, "40" }, + { 0x0A, "44" }, + { 0x0B, "48" }, + { 0x0C, "52" }, + { 0x0D, "56" }, + { 0x0E, "60" }, + { 0x0F, "64" }, + { 0, NULL } }; + +static guint16 +de_rr_cell_opt_bcch(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CELL_OPT_BCCH].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_OPT_BCCH]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pwrc, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_dtx_bcch, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_radio_link_timeout, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.3a Cell Options (SACCH) + */ +static const value_string gsm_a_rr_dtx_sacch_vals[] = { + { 0x00, "The MS may use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x01, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x02, "The MS shall not use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x03, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS may use uplink discontinuous transmission on TCH-H" }, + { 0x04, "The MS may use uplink discontinuous transmission on a TCH-F. The MS may use uplink discontinuous transmission on TCH-H" }, + { 0x05, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0x06, "The MS shall not use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0x07, "The MS may use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0, NULL } }; + +static guint16 +de_rr_cell_opt_sacch(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint8 oct; + guint8 dtx; + guint32 curr_offset; + + curr_offset = offset; + + oct = tvb_get_guint8(tvb, curr_offset); + dtx = ((oct&0x80)>>5)|((oct&0x30)>>4); /* DTX is a split filed in bits 8, 6 and 5 */ + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CELL_OPT_SACCH].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_OPT_SACCH]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pwrc, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree, hf_gsm_a_rr_dtx_sacch, tvb, curr_offset, 1, dtx); + proto_tree_add_item(subtree, hf_gsm_a_rr_radio_link_timeout, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.4 Cell Selection Parameters + */ +static guint16 +de_rr_cell_sel_param(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 2, "%s", + gsm_rr_elem_strings[DE_RR_CELL_SEL_PARAM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_SEL_PARAM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_cell_reselect_hyst, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_ms_txpwr_max_cch, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_acs, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_neci, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_rxlev_access_min, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.4a MAC Mode and Channel Coding Requested + * [3] 10.5.2.5 Channel Description + */ +guint16 +de_rr_ch_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8,subchannel; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + + if ((oct8 & 0xf8) == 0x08) + { + str = "TCH/F + ACCHs"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else + { + if ((oct8 & 0xf0) == 0x10) + { + str = "TCH/H + ACCHs, Subchannel"; + subchannel = ((oct8 & 0x08)>>3); + } + else if ((oct8 & 0xe0) == 0x20) + { + str = "SDCCH/4 + SACCH/C4 or CBCH (SDCCH/4), Subchannel"; + subchannel = ((oct8 & 0x18)>>3); + } + else if ((oct8 & 0xc0) == 0x40) + { + str = "SDCCH/8 + SACCH/C8 or CBCH (SDCCH/8), Subchannel"; + subchannel = ((oct8 & 0x38)>>3); + } else { + str = "Unknown channel information"; + subchannel = oct8; + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s %d",a_bigbuf,str,subchannel); + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.5a Channel Description 2 + */ +static guint16 +de_rr_ch_dsc2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8,subchannel; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC2].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC2]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + + if ((oct8 & 0xf8) == 0x0) + { + str = "TCH/F + FACCH/F and SACCH/M"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else if ((oct8 & 0xf8) == 0x08) + { + str = "TCH/F + FACCH/F and SACCH/F"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else if ((oct8 & 0xf8) == 0xf0) + { + str = "TCH/F + FACCH/F and SACCH/M + bi- and unidirectional channels"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else + { + if ((oct8 & 0xf0) == 0x10) + { + str = "TCH/H + ACCHs, Subchannel"; + subchannel = ((oct8 & 0x08)>>3); + } + else if ((oct8 & 0xe0) == 0x20) + { + str = "SDCCH/4 + SACCH/C4 or CBCH (SDCCH/4), Subchannel"; + subchannel = ((oct8 & 0x18)>>3); + } + else if ((oct8 & 0xc0) == 0x40) + { + str = "SDCCH/8 + SACCH/C8 or CBCH (SDCCH/8), Subchannel"; + subchannel = ((oct8 % 0x38)>>3); + } + else if ((oct8 & 0xc0) == 0x80) + { + str = "TCH/F + FACCH/F and SACCH/M + bidirectional channels at timeslot"; + subchannel = ((oct8 % 0x38)>>3); + } + else if ((oct8 & 0xe0) == 0xc0) + { + str = "TCH/F + FACCH/F and SACCH/M + unidirectional channels at timeslot"; + subchannel = ((oct8 % 0x38)>>3); + } else { + str = "Unknown channel information"; + subchannel = oct8; + } + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s %d",a_bigbuf,str,subchannel); + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.5c Channel Description 3 + */ +static guint16 +de_rr_ch_dsc3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC3].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC3]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.6 Channel Mode + */ +/* Channel Mode */ +static const value_string gsm_a_rr_channel_mode_vals[] = { + { 0x00, "signalling only"}, + { 0x01, "speech full rate or half rate version 1(GSM FR or GSM HR)"}, + { 0x21, "speech full rate or half rate version 2(GSM EFR)"}, + { 0x41, "speech full rate or half rate version 3(FR AMR or HR AMR)"}, + { 0x81, "speech full rate or half rate version 4(OFR AMR-WB or OHR AMR-WB)"}, + { 0x82, "speech full rate or half rate version 5(FR AMR-WB )"}, + { 0x83, "speech full rate or half rate version 6(OHR AMR )"}, + { 0x61, "data, 43.5 kbit/s (downlink)+14.5 kbps (uplink)"}, + { 0x62, "data, 29.0 kbit/s (downlink)+14.5 kbps (uplink)"}, + { 0x64, "data, 43.5 kbit/s (downlink)+29.0 kbps (uplink)"}, + { 0x67, "data, 14.5 kbit/s (downlink)+43.5 kbps (uplink)"}, + { 0x65, "data, 14.5 kbit/s (downlink)+29.0 kbps (uplink)"}, + { 0x66, "data, 29.0 kbit/s (downlink)+43.5 kbps (uplink)"}, + { 0x27, "data, 43.5 kbit/s radio interface rate"}, + { 0x63, "data, 32.0 kbit/s radio interface rate"}, + { 0x43, "data, 29.0 kbit/s radio interface rate"}, + { 0x0f, "data, 14.5 kbit/s radio interface rate"}, + { 0x03, "data, 12.0 kbit/s radio interface rate"}, + { 0x0b, "data, 6.0 kbit/s radio interface rate"}, + { 0x13, "data, 3.6 kbit/s radio interface rate"}, + { 0, NULL } +}; + +guint16 +de_rr_ch_mode(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_channel_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.7 Channel Mode 2 + */ + +static const value_string gsm_a_rr_channel_mode2_vals[] = { + { 0x00, "signalling only"}, + { 0x05, "speech half rate version 1(GSM HR)"}, + { 0x25, "speech half rate version 2(GSM EFR)"}, + { 0x45, "speech half rate version 3(HR AMR)"}, + { 0x85, "speech half rate version 4(OHR AMR-WB)"}, + { 0x06, "speech half rate version 6(OHR AMR )"}, + { 0x0f, "data, 6.0 kbit/s radio interface rate"}, + { 0x17, "data, 3.6 kbit/s radio interface rate"}, + { 0, NULL } +}; + +static guint16 +de_rr_ch_mode2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_channel_mode2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.7a UTRAN Classmark information element + */ +static guint16 +de_rr_utran_cm(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + tvbuff_t *rrc_irat_ho_info_tvb; + + curr_offset = offset; + if (len) + { + rrc_irat_ho_info_tvb = tvb_new_subset(tvb, curr_offset, len, len); + if (rrc_irat_ho_info_handle) + call_dissector(rrc_irat_ho_info_handle, rrc_irat_ho_info_tvb, pinfo, tree); + } + + curr_offset += len; + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.7b (void) + */ + +/* + * [3] 10.5.2.7c Classmark Enquiry Mask + * Bit 8: + * 0 CLASSMARK CHANGE message is requested + * 1 CLASSMARK CHANGE message is not requested + * Bits 7-5 . 5 + * 000 UTRAN CLASSMARK CHANGE message including status on predefined configurations (i.e. Sequence Description) is requested + * 111 UTRAN CLASSMARK CHANGE message including status on predefined configurations (i.e. Sequence Description) is not requested. + * All other values shall not be sent. If received, they shall be interpreted as '000'. + * Bit 4: + * 0 CDMA2000 CLASSMARK CHANGE message requested + * 1 CDMA2000 CLASSMARK CHANGE message not requested. + * Bit 3: + * 0 GERAN IU MODE CLASSMARK CHANGE message requested + * 1 GERAN IU MODE CLASSMARK CHANGE message not requested. + * Bits 2 - 1: spare(0). + */ +static const true_false_string gsm_a_msg_req_value = { + "message is not requested", + "message is requested" +}; + +static const value_string gsm_a_rr_utran_cm_cng_msg_req_vals[] = { + { 0x0, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x1, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x2, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x3, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x4, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x5, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x6, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x7, "message including status on predefined configurations (i.e. Sequence Description) is not requested."}, + { 0, NULL } +}; +guint16 +de_rr_cm_enq_mask(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_utran_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_cdma200_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_geran_iu_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.8 Channel Needed + */ +static const value_string gsm_a_rr_channel_needed_vals[] = { + { 0x00, "Any channel"}, + { 0x01, "SDCCH"}, + { 0x02, "TCH/F (Full rate)"}, + { 0x03, "TCH/H or TCH/F (Dual rate)"}, + { 0, NULL } +}; +guint16 +de_rr_chnl_needed(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CHNL_NEEDED].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CHNL_NEEDED]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch1, tvb, (curr_offset<<3)+bit_offset+2, 2, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch2, tvb, (curr_offset<<3)+bit_offset, 2, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.8a Channel Request Description + * [3] 10.5.2.8b Channel Request Description 2 + */ +/* + * [3] 10.5.2.9 Cipher Mode Setting + */ +/* SC (octet 1) */ +static const value_string gsm_a_rr_sc_vals[] = { + { 0, "No ciphering"}, + { 1, "Start ciphering"}, + { 0, NULL } +}; +/* algorithm identifier + * If SC=1 then: + * bits + * 4 3 2 + */ +guint16 +de_rr_cip_mode_set(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint64 value; + + curr_offset = offset; + + /* Cipher Mode Setting + * Note: The coding of fields SC and algorithm identifier is defined in [44.018] + * as part of the Cipher Mode Setting IE. + */ + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + proto_tree_add_bits_ret_val(tree, hf_gsm_a_rr_sc, tvb, (curr_offset<<3)+bit_offset+3, 1, &value, ENC_BIG_ENDIAN); + if (value == 1){ /* Start ciphering */ + /* algorithm identifier */ + proto_tree_add_bits_item(tree, hf_gsm_a_algorithm_id, tvb, (curr_offset<<3)+bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.10 Cipher Response + */ +/* CR (octet 1) */ +static const value_string gsm_a_rr_cr_vals[] = { + { 0, "IMEISV shall not be included"}, + { 1, "IMEISV shall be included"}, + { 0, NULL } +}; + +static guint16 +de_rr_cip_mode_resp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + /* Cipher Mode Response + * Note: The coding of field CR is defined in [44.018] + * as part of the Cipher Mode Response IE. + */ + proto_tree_add_bits_item(tree, hf_gsm_a_rr_cr, tvb, (curr_offset<<3)+bit_offset+3, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* [3] 10.5.2.11 Control Channel Description */ + +static const value_string gsm_a_rr_mscr_vals[] = { + { 0, "MSC is Release '98 or older"}, + { 1, "MSC is Release '99 onwards"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_att_vals[] = { + { 0, "MSs in the cell are not allowed to apply IMSI attach and detach procedure"}, + { 1, "MSs in the cell shall apply IMSI attach and detach procedure"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_ccch_conf_vals[] = { + { 0, "1 basic physical channel used for CCCH, not combined with SDCCHs"}, + { 1, "1 basic physical channel used for CCCH, combined with SDCCHs"}, + { 2, "2 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 3, "Reserved"}, + { 4, "3 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 5, "Reserved"}, + { 6, "4 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 7, "Reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cbq3_vals[] = { + { 0, "Iu mode not supported"}, + { 1, "Iu mode capable MSs barred"}, + { 2, "Iu mode supported, cell not barred"}, + { 3, "Iu mode supported, cell not barred"}, + { 0, NULL } +}; + +static guint16 +de_rr_ctrl_ch_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint8 oct; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_CTRL_CH_DESC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CTRL_CH_DESC]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_mscr, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_att, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_bs_ag_blks_res, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_ccch_conf, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + oct = tvb_get_guint8(tvb, curr_offset); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq3, tvb, (curr_offset<<3)+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree, hf_gsm_a_rr_bs_pa_mfrms, tvb, curr_offset, 1, (oct&0x07)+2); + + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_t3212, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* [3] 10.5.2.11a DTM Information Details + */ +/* + * [3] 10.5.2.11b Dynamic ARFCN Mapping + */ +static const value_string gsm_a_rr_gsm_band_vals[] = { + { 0, "GSM 750"}, + { 1, "DCS 1800"}, + { 2, "PCS 1900"}, + { 3, "GSM T 380"}, + { 4, "GSM T 410"}, + { 5, "GSM T 900"}, + { 6, "GSM 710"}, + { 7, "GSM T 810"}, + { 8, "Reserved"}, + { 9, "Reserved"}, + { 10, "Reserved"}, + { 11, "Reserved"}, + { 12, "Reserved"}, + { 13, "Reserved"}, + { 14, "Reserved"}, + { 15, "Reserved"}, + { 0, NULL } +}; + + +static guint16 +de_rr_dyn_arfcn_map(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint64 length; + guint value; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_ret_val(tree, hf_gsm_a_rr_dyn_arfcn_length, tvb, bit_offset, 8, &length, ENC_BIG_ENDIAN); + value = gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating Dynamic ARFCN Mapping", "Present", "Not Present"); + while (value && length) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_gsm_band, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_arfcn_first, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_band_offset, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_arfcn_range, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + length -= 4; + } + + curr_offset = curr_offset + len; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.12 Frequency Channel Sequence + */ +static guint16 +de_rr_freq_ch_seq(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset, i; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_lowest_arfcn, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset += 1; + bit_offset = curr_offset << 3; + for (i=0; i<16; i++) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_inc_skip_arfcn, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + + curr_offset = curr_offset + 8; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.13 Frequency List + */ +/* + * [3] 10.5.2.13 Frequency List + * + * Bit Bit Bit Bit Bit format notation + * 8 7 4 3 2 + * 0 0 X X X bit map 0 + * 1 0 0 X X 1024 range + * 1 0 1 0 0 512 range + * 1 0 1 0 1 256 range + * 1 0 1 1 0 128 range + * 1 0 1 1 1 variable bit map + */ +/* The mask 0xce (1100 1110) will produce the result 0110 0111*/ +static const value_string gsm_a_rr_freq_list_format_id_vals[] = { + { 0x00, "bit map 0"}, + { 0x02, "bit map 0"}, + { 0x04, "bit map 0"}, + { 0x06, "bit map 0"}, + { 0x08, "bit map 0"}, + { 0x0a, "bit map 0"}, + { 0x0c, "bit map 0"}, + { 0x0e, "bit map 0"}, + { 0x40, "1024 range"}, + { 0x41, "1024 range"}, + { 0x42, "1024 range"}, + { 0x43, "1024 range"}, + { 0x44, "512 range"}, + { 0x45, "256 range"}, + { 0x46, "128 range"}, + { 0x47, "variable bit map"}, + { 0x00, NULL } +}; + +static guint16 +de_rr_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, len, add_string, string_len); +} +/* + * [3] 10.5.2.14 Frequency Short List + * + *The Frequency Short List information element is a type 3 information element of 10 octet length. + * + * This element is encoded exactly as the Frequency List information element, + * except that it has a fixed length instead of a variable length and does + * not contain a length indicator and that it shall not be encoded in bitmap 0 format. + */ + + static guint16 +de_rr_freq_short_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) + { + return dissect_arfcn_list(tvb, tree, pinfo, offset, 9, add_string, string_len); + } + +/* + * [3] 10.5.2.14a Frequency Short List 2 + * + * The Frequency Short List information element is a type 3 information element of 8 octet length. + * + * This element is encoded exactly as the Frequency List information element, + * except that it has a fixed length instead of a variable length and does + * not contain a length indicator and that it shall not be encoded in bitmap 0 format. + */ +static guint16 +de_rr_freq_short_list2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, 8, add_string, string_len); +} +/* + * [3] 10.5.2.14b Group Channel Description + */ + +/* + * [3] 10.5.2.14c GPRS Resumption + */ +static const true_false_string gsm_a_rr_gprs_resumption_ack_value = { + "Resumption of GPRS services successfully acknowledged", + "Resumption of GPRS services not successfully acknowledged" +}; + +static guint16 +de_rr_gprs_resumption(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_gprs_resumption_ack, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset += 1; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.14d GPRS broadcast information + */ + +static gint +de_rr_rest_oct_gprs_cell_options(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, curr_bit_offset_sav; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nmo, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t3168, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t3192, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_drx_timer_max, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_access_burst_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_control_ack_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bs_cv_max, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PAN bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_dec, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_inc, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_max, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Optional Extensions", "Present", "Not Present")) + { /* Optional extension information */ + curr_bit_offset_sav = curr_bit_offset; + item2 = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO]); + value = tvb_get_bits8(tvb,curr_bit_offset,6); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 1, "Extension Length: %d", value); + curr_bit_offset += 6; + value += 1; + proto_item_set_len(item2,((curr_bit_offset+value-curr_bit_offset_sav)>>3)+1); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "EGPRS", "supported by cell", "not supported by cell")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_egprs_packet_channel_request, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bep_period, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + value -= 5; + } + value -= 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pfc_feature_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_dtm_support, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bss_paging_coordination, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 3; + if (value > 0) + { /* Rel 4 extension */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_ccn_active, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nw_ext_utbf, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 2; + if (value > 0) + { /* Rel 6 extension */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_multiple_tbf_capability, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_ext_utbf_no_data, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_dtm_enhancements_capability, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 3; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MBMS procedures", "supported by cell", "not supported by cell")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dedicated_mode_mbms_notification_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mnci_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + value -= 2; + } + else + bit_offset += 1; + value -= 1; + if (value > 0) + { /* Rel 7 extension */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reduced_latency_access, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + value -= 1; + } + } + } + curr_bit_offset += value; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_rest_oct_gprs_power_control_parameters(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t_avg_w, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t_avg_t, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pc_meas_chan, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_n_avg_i, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static guint16 +de_rr_gprs_broadcast_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len , gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + bit_offset += de_rr_rest_oct_gprs_cell_options(tvb, tree, bit_offset); + bit_offset += de_rr_rest_oct_gprs_power_control_parameters(tvb, tree, bit_offset); + curr_offset += len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.15 Handover Reference + */ +static guint16 +de_rr_ho_ref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_HO_REF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_HO_REF]); + + /* Handover reference value */ + proto_tree_add_item(subtree, hf_gsm_a_rr_ho_ref_val, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +static const value_string gsm_a_access_tech_type_vals[] = { + { 0, "GSM P"}, + { 1, "GSM E --note that GSM E covers GSM P"}, + { 2, "GSM R --note that GSM R covers GSM E and GSM P"}, + { 3, "GSM 1800"}, + { 4, "GSM 1900"}, + { 5, "GSM 450"}, + { 6, "GSM 480"}, + { 7, "GSM 850"}, + { 8, "GSM 750"}, + { 9, "GSM T 380"}, + { 10, "GSM T 410"}, + { 11, "GSM T 900"}, + { 12, "GSM 710"}, + { 13, "GSM T 810"}, + { 14, "reserved"}, + { 15, "Indicates the presence of a list of Additional access technologies"}, + { 0, NULL } +}; + + +static const value_string gsm_a_egprs_mcs_vals[] = { + { 0, "MCS-1"}, + { 1, "MCS-2"}, + { 2, "MCS-3"}, + { 3, "MCS-4"}, + { 4, "MCS-5"}, + { 5, "MCS-6"}, + { 6, "MCS-7"}, + { 7, "MCS-8"}, + { 8, "MCS-9"}, + { 9, "MCS-5-7"}, + { 10, "MCS-6-9"}, + { 11, "reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_gprs_cs_vals[] = { + { 0, "CS-1"}, + { 1, "CS-2"}, + { 2, "CS-3"}, + { 3, "CS-4"}, + { 0, NULL } +}; + + +static const true_false_string gsm_a_tlli_block_channel_coding_vals = { + "mobile station shall use coding scheme as specified by the corresponding CHANNEL CODING COMMAND or EGPRS CHANNEL CODING COMMAND field", + "mobile station shall use CS-1 in GPRS TBF mode or MCS-1 in EGPRS TBF mode" +}; + +static const true_false_string gsm_a_resegment_vals = { + "Retransmitted RLC data blocks shall be re-segmented according to commanded MCS", + "Retransmitted RLC data blocks shall not be re-segmented" +}; + +static const true_false_string gsm_a_polling_vals = { + "MS shall send a PACKET CONTROL ACKNOWLEDGEMENT message in the uplink block specified by TBF Starting Time, on the assigned PDCH", + "no action is required from MS" +}; + +static const true_false_string gsm_a_usf_granularity_vals = { + "the mobile station shall transmit four consecutive RLC/MAC blocks", + "the mobile station shall transmit one RLC/MAC block" +}; + +static const true_false_string gsm_a_rlc_mode_vals = { + "RLC unacknowledged mode", + "RLC acknowledged mode" +}; + +static const true_false_string gsm_a_ta_valid_vals = { + "the timing advance value is valid", + "the timing advance value is not valid" +}; + + + +static const value_string gsm_a_egprs_windows_size_vals[] = { + { 0, "64"}, + { 1, "96"}, + { 2, "128"}, + { 3, "160"}, + { 4, "192"}, + { 5, "224"}, + { 6, "256"}, + { 7, "288"}, + { 8, "320"}, + { 9, "352"}, + { 10, "384"}, + { 11, "416"}, + { 12, "448"}, + { 13, "480"}, + { 14, "512"}, + { 15, "544"}, + { 16, "576"}, + { 17, "608"}, + { 18, "640"}, + { 19, "672"}, + { 20, "704"}, + { 21, "736"}, + { 22, "768"}, + { 23, "800"}, + { 24, "832"}, + { 25, "864"}, + { 26, "896"}, + { 27, "928"}, + { 28, "960"}, + { 29, "992"}, + { 30, "1024"}, + { 31, "reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_gamma_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + { 10, "20 dB"}, + { 11, "22 dB"}, + { 12, "24 dB"}, + { 13, "26 dB"}, + { 14, "28 dB"}, + { 15, "30 dB"}, + { 16, "32 dB"}, + { 17, "34 dB"}, + { 18, "36 dB"}, + { 19, "38 dB"}, + { 20, "40 dB"}, + { 21, "42 dB"}, + { 22, "44 dB"}, + { 23, "46 dB"}, + { 24, "48 dB"}, + { 25, "50 dB"}, + { 26, "52 dB"}, + { 27, "54 dB"}, + { 28, "56 dB"}, + { 29, "58 dB"}, + { 30, "60 dB"}, + { 31, "62 dB"}, + { 0, NULL } +}; + + + +static const value_string gsm_a_link_quality_meas_mode_vals[] = { + { 0, "The MS shall not report either interference measurements or per slot BEP measurements"}, + { 1, "The MS shall report available interference measurements for timeslots 0 through 7"}, + { 2, "The MS shall report mean BEP on each assigned time slot ... No interference measurements shall be reported..."}, + { 3, "The MS shall report mean BEP on each assigned time slot ... In addition to mean BEP, the MS shall report interference measurements for no more than four time slots..."}, + { 0, NULL } +}; + + +static gint +de_rr_ia_rest_oct_egprs_packet_uplink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Access Technologies Request", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_access_tech_type, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "", "TFI Assignment Present", "Multi Block Allocation Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Allocation Type", "Fixed Allocation (not to be used after Rel-4)", "Dynamic Allocation (mandatory after Rel-4)")) + { + gint8 bitmap_len = tvb_get_bits8(tvb, curr_bit_offset, 5); + gint32 bitmap; + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+5)>>3) - (curr_bit_offset>>3), "Bitmap length %d", bitmap_len); + curr_bit_offset += 5; + bitmap = tvb_get_bits32(tvb, curr_bit_offset, bitmap_len, ENC_BIG_ENDIAN); + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+bitmap_len)>>3) - (curr_bit_offset>>3), "Bitmap %d", bitmap); + curr_bit_offset += bitmap_len; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_mcs, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli_block_channel_coding, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BEP_PERIOD2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bep_period2, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_resegment, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_window_size, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + } + else /* Multi Block Allocation */ + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_num_of_radio_block_allocated, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BTS Power Control Mode", "Mode B (not to be used after Rel-4)", "Mode A (mandatory after Rel-4)"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for Rel-7", "Present", "Not present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +static gint +de_rr_ia_rest_oct_tmgi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint16 value16; + gchar mcc[4]; + gchar mnc[4]; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_TMGI].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TMGI]); + + if (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MCC and MNC Parameters", "Present", "Not Present")) /* without MCC and MNC parameters */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mbms_service_id, tvb, curr_bit_offset, 24, ENC_BIG_ENDIAN); + curr_bit_offset += 24; + } + else /* with MCC and MNC parameters */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mbms_service_id, tvb, curr_bit_offset, 24, ENC_BIG_ENDIAN); + curr_bit_offset += 24; + + value16 = tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN); + mcc[0] = '0' + ((value16>>8)&0xf); + mcc[1] = '0' + ((value16>>4)&0xf); + mcc[2] = '0' + ((value16 )&0xf); + mcc[3] = '\0'; + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 12, + "Mobile Country Code (MCC): %s", + mcc); + curr_bit_offset += 12; + + value16 = tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN); + mnc[0] = '0' + ((value16>>8)&0xf); + mnc[1] = '0' + ((value16>>4)&0xf); + mnc[2] = '0' + ((value16 )&0xf); + mnc[3] = '\0'; + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 12, + "Mobile Network Code (MNC): %s", + mnc); + curr_bit_offset += 12; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + +static gint +de_rr_ia_rest_oct_packet_timing_advance(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE]); + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Value", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index and Timeslot", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_timeslot_num, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + +static gint +de_rr_ia_rest_oct_multiple_blocks_packet_downlink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_num_of_radio_block_allocated, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "", "default 0 bit", "Reserved Value")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MBMS Assignment", "(Non-distribution)", "(Distribution)")) + {/* MBMS Assignment (Distribution) */ + curr_bit_offset += de_rr_ia_rest_oct_tmgi(tvb, tree, curr_bit_offset); + } + else /* MBMS Assignment (Non-distribution) */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli, tvb, curr_bit_offset, 32, ENC_BIG_ENDIAN); + curr_bit_offset += 32; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MS Parameters", "Present", "Not Present")) + { + value = tvb_get_bits8(tvb,curr_bit_offset,2); + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 2, + "Length Indicator of MS ID: %d", + value); + curr_bit_offset += 2; + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ms_id, tvb, curr_bit_offset, value+1, ENC_BIG_ENDIAN); + curr_bit_offset += value+1; + curr_bit_offset += de_rr_ia_rest_oct_packet_timing_advance(tvb, tree, curr_bit_offset); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Gamma", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + } + } + } + + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + + +static gint +de_rr_ia_rest_oct_packet_uplink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT]); + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Packet Uplink Assignment", "Normal", "Single Block")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Allocation Type", "Fixed Allocation (not to be used after Rel-4))", "Dynamic Allocation (mandatory after Rel-4)")) + { + gint8 bitmap_len = tvb_get_bits8(tvb, curr_bit_offset, 5); + gint32 bitmap; + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+5)>>3) - (curr_bit_offset>>3), "Bitmap length %d", bitmap_len); + curr_bit_offset += 5; + bitmap = tvb_get_bits32(tvb, curr_bit_offset, bitmap_len, ENC_BIG_ENDIAN); + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+bitmap_len)>>3) - (curr_bit_offset>>3), "Bitmap %d", bitmap); + curr_bit_offset += bitmap_len; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_cs, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli_block_channel_coding, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + } + else /* Single Block Allocation */ + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + /* fixed bits '01' */ + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Early R97 version", "Later than R97 version"); + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Later than R97 version", "Early R97 version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + /* The value '1' was allocated in an earlier version of the protocol and shall not be used */ + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for Rel-6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +static gint +de_rr_ia_rest_oct_packet_downlink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli, tvb, curr_bit_offset, 32, ENC_BIG_ENDIAN); + curr_bit_offset += 32; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TFI Assignment (etc)", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rlc_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ta_valid, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BTS Power Control Mode", "Mode B (not to be used after Rel-4)", "Mode A (mandatory after Rel-4)"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_window_size, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_link_quality_meas_mode, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BEP_PERIOD2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bep_period2, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree,0 ,curr_bit_offset++,"Additions for Rel-6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + + + +static gint +de_rr_ia_rest_oct_second_part_packet_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT]); + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +/* + * [3] 10.5.2.16 IA Rest Octets + */ + +static guint16 +de_rr_ia_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + gint length; + guint64 ma_length; + guint8 tvb_len = tvb_length(tvb); + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_IA_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IA_REST_OCT]); + + bit_offset = curr_offset << 3; + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"first discriminator bit", "High", "Low") == FALSE) + { + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"second discriminator bit", "High", "Low") == FALSE) + { + /* LL */ + gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"A compressed version of the INTER RAT HANDOVER INFO message ", "shall be used", "shall not be used"); + } + else /* LH */ + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "reserved for future use", "EGPRS Packet Uplink Assignment or Multiple blocks Packet Downlink Assignment")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "", "Multiple blocks Packet Downlink Assignment", "EGPRS Packet Uplink Assignment")) + { + bit_offset += de_rr_ia_rest_oct_egprs_packet_uplink_assignment(tvb, tree, bit_offset); + } + else + { + bit_offset += de_rr_ia_rest_oct_multiple_blocks_packet_downlink_assignment(tvb, tree, bit_offset); + } + } + else + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "reserved for future use (however the value 7C for the first octet shall not be used)"); + } + } + } + else + { + if (0 == gsm_rr_csn_HL_flag(tvb,subtree, 0,bit_offset++,"second discriminator bit:", "High", "Low")) + { + /* HL */ + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_ma_length, tvb, bit_offset, 6, &ma_length, ENC_BIG_ENDIAN); + bit_offset += 6; + /* Frequency Parameters, before time */ + if (ma_length>0) + { + /* two '0' bits */ + bit_offset += 2; + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "MAIO: %d", tvb_get_bits8(tvb,bit_offset,6)); + bit_offset += 6; + length = (gint)ma_length; + item = proto_tree_add_text(subtree,tvb, bit_offset>>3, (length>>3)-1, "MA Bitmap: "); + length = (length-1)*8; + while (length) + { + proto_item_append_text(item,"%d",tvb_get_bits8(tvb,bit_offset,1)); + bit_offset += 1; + length -= 1; + } + } + gsm_rr_csn_HL_flag(tvb,subtree, 0,bit_offset++,"A compressed version of the INTER RAT HANDOVER INFO message", "shall be used", "shall not be used"); + } + else /* HH */ + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "Second Part Packet Assignment", "Packet Uplik Assignment or Packet Downlink Assignment")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "Packet Downlink Assignment", "Packet Uplink Assignment")) + { + /* 00 < Packet Uplink Assignment > */ + bit_offset += de_rr_ia_rest_oct_packet_uplink_assignment(tvb, tree, bit_offset); + } + else /* 01 < Packet Downlink Assignment > */ + { + bit_offset += de_rr_ia_rest_oct_packet_downlink_assignment(tvb, tree, bit_offset); + } + } + else /* 1 < Second Part Packet Assignment > */ + { + bit_offset += de_rr_ia_rest_oct_second_part_packet_assignment(tvb, tree, bit_offset); + } + } + } + + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.17 IAR Rest Octets + */ + +static guint16 +de_rr_iar_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_bit_offset; + guint8 i; + guint8 tvb_len = tvb_length(tvb); + + curr_bit_offset = offset<<3; + + item = + proto_tree_add_text(tree, + tvb, offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_IAR_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IAR_REST_OCT]); + + for( i=0; i<4; i++ ) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "Extended RA %d present", i); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + else + { + proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "Extended RA %d not present", i); + } + } + gsm_rr_csn_spare_bits(tree, tvb, curr_bit_offset); + return tvb_len - offset; +} + +/* + * [3] 10.5.2.18 IAX Rest Octets + */ +static guint16 +de_rr_iax_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_bit_offset; + guint8 tvb_len = tvb_length(tvb); + + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_bit_offset = offset<<3; + + item = + proto_tree_add_text(tree, + tvb, offset, len, "%s", + gsm_rr_elem_strings[DE_RR_IAX_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IAX_REST_OCT]); + + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "A compressed version of the INTER RAT HANDOVER INFO message", "shall be used", "shall not be used"); + + gsm_rr_csn_spare_bits(tree, tvb, curr_bit_offset); + return tvb_len - offset; +} + +/* + * [3] 10.5.2.19 L2 Pseudo Length + */ +static guint16 +de_rr_l2_pseudo_len(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 1, "%s", gsm_rr_elem_strings[DE_RR_L2_PSEUDO_LEN].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_L2_PSEUDO_LEN]); + + /* L2 Pseudo Length value */ + proto_tree_add_item(subtree, hf_gsm_a_rr_L2_pseudo_len, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.20 Measurement Results + */ +static const true_false_string gsm_a_rr_dtx_vals = { + "DTX was used", + "DTX was not used" +}; + + +static const true_false_string gsm_a_rr_mv_vals = { + "The measurement results are not valid", + "The measurement results are valid" +}; + +static const value_string gsm_a_rr_rxqual_vals [] = { + {0, "BER < 0.2%, Mean value 0.14%"}, + {1, "0.2% <= BER < 0.4%, Mean value 0.28%"}, + {2, "0.4% <= BER < 0.8%, Mean value 0.57%"}, + {3, "0.8% <= BER < 1.6%, Mean value 1.13%"}, + {4, "1.6% <= BER < 3.2%, Mean value 2.26%"}, + {5, "3.2% <= BER < 6.4%, Mean value 4.53%"}, + {6, "6.4% <= BER < 12.8%, Mean value 9.05%"}, + {7, "BER > 12.8%, Mean value 18.10%"}, + {0, NULL} +}; + +static const value_string gsm_a_rr_ncell_vals [] = { + {0, "No neighbour cell measurement result"}, + {1, "1 neighbour cell measurement result"}, + {2, "2 neighbour cell measurement result"}, + {3, "3 neighbour cell measurement result"}, + {4, "4 neighbour cell measurement result"}, + {5, "5 neighbour cell measurement result"}, + {6, "6 neighbour cell measurement result"}, + {7, "Neighbour cell information not available for serving cell"}, + {0, NULL} +}; + +guint16 +de_rr_meas_res(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + guint64 no_ncell_m; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 16, "%s", + gsm_rr_elem_strings[DE_RR_MEAS_RES].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_MEAS_RES]); + + /* 2nd octet */ + /* BA-USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ba_used, tvb, curr_offset<<3, 1, ENC_BIG_ENDIAN); + /* DTX USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dtx_used, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + /* RXLEV-FULL-SERVING-CELL */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_full_serv_cell, tvb, (curr_offset<<3)+2, 6, ENC_BIG_ENDIAN); + curr_offset++; + + /* 3rd octet */ + /* 3G-BA-USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ba_used, tvb, curr_offset<<3, 1, ENC_BIG_ENDIAN); + /* MEAS-VALID */ + proto_tree_add_item(subtree, hf_gsm_a_rr_meas_valid, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* RXLEV-SUB-SERVING-CELL */ + proto_tree_add_item(subtree, hf_gsm_a_rr_rxlev_sub_serv_cell, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset++; + + /* 4th octet */ + /* RXQUAL-FULL-SERVING-CELL */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxqual_full_serv_cell, tvb, (curr_offset<<3)+1, 3, ENC_BIG_ENDIAN); + + /* RXQUAL-SUB-SERVING-CELL */ + proto_tree_add_item(subtree, hf_gsm_a_rr_rxqual_sub_serv_cell, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* NO-NCELL-M */ + bit_offset = (curr_offset << 3) + 7; + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_no_ncell_m, tvb, bit_offset, 3, &no_ncell_m, ENC_BIG_ENDIAN); + bit_offset += 3; + if (no_ncell_m == 7) /* No neighbour cell information available) */ + no_ncell_m = 0; + while (no_ncell_m) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_freq_ncell, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + no_ncell_m -= 1; + } + + return(len); +} + +/* + * [3] 10.5.2.20a GPRS Measurement Results + */ +/* + * [3] 10.5.2.21 Mobile Allocation + */ +static guint16 +de_rr_mob_all(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + proto_item *item; + gint i, j; + guint8 value; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "Bitmap of increasing ARFCNs included in the Mobile Allocation: "); + for(i=len; i>0; i--) + { + value = tvb_get_guint8(tvb,curr_offset+i-1); + for (j=0; j<8; j++) + { + proto_item_append_text(item,"%d",(value>>j)&0x01); + } + } + + curr_offset = curr_offset + len; + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.21a Mobile Time Difference + */ +static guint16 +de_rr_mob_time_diff(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_mobile_time_difference, tvb, curr_offset, len, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + len; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21aa MultiRate configuration + */ +/* Multirate speech version Octet 3 Bits 8 7 6 */ +static const value_string multirate_speech_ver_vals[] = { + { 1, "Adaptive Multirate speech version 1"}, + { 2, "Adaptive Multirate speech version 2"}, + { 0, NULL } +}; +/* Bit 5 NSCB: Noise Suppression Control Bit */ +static const value_string NSCB_vals[] = { + { 0, "Noise Suppression can be used (default)"}, + { 1, "Noise Suppression shall be turned off"}, + { 0, NULL } +}; +/* Bit 4 ICMI: Initial Codec Mode Indicator */ +static const value_string ICMI_vals[] = { + { 0, "The initial codec mode is defined by the implicit rule provided in 3GPP TS 05.09"}, + { 1, "The initial codec mode is defined by the Start Mode field"}, + { 0, NULL } +}; +/* +Table 10.5.2.21aa.2: Set of adaptive multirate codec modes field (octet 4) +for the Multirate speech version 1 +*/ +static const true_false_string gsm_a_rr_set_of_amr_codec_modes = { + "is part of the subset", + "is not part of the subset" +}; + +static const value_string gsm_a_rr_amr_threshold_vals[] = { + { 0, "0.0 dB"}, + { 1, "0.5 dB"}, + { 2, "1.0 dB"}, + { 3, "1.5 dB"}, + { 4, "2.0 dB"}, + { 5, "2.5 dB"}, + { 6, "3.0 dB"}, + { 7, "3.5 dB"}, + { 8, "4.0 dB"}, + { 9, "4.5 dB"}, + { 10, "5.0 dB"}, + { 11, "5.5 dB"}, + { 12, "6.0 dB"}, + { 13, "6.5 dB"}, + { 14, "7.0 dB"}, + { 15, "7.5 dB"}, + { 16, "8.0 dB"}, + { 17, "8.5 dB"}, + { 18, "9.0 dB"}, + { 19, "9.5 dB"}, + { 20, "10.0 dB"}, + { 21, "10.5 dB"}, + { 22, "11.0 dB"}, + { 23, "11.5 dB"}, + { 24, "12.0 dB"}, + { 25, "12.5 dB"}, + { 26, "13.0 dB"}, + { 27, "13.5 dB"}, + { 28, "14.0 dB"}, + { 29, "14.5 dB"}, + { 30, "15.0 dB"}, + { 31, "15.5 dB"}, + { 32, "16.0 dB"}, + { 33, "16.5 dB"}, + { 34, "17.0 dB"}, + { 35, "17.5 dB"}, + { 36, "18.0 dB"}, + { 37, "18.5 dB"}, + { 38, "19.0 dB"}, + { 39, "19.5 dB"}, + { 40, "20.0 dB"}, + { 41, "20.5 dB"}, + { 42, "21.0 dB"}, + { 43, "21.5 dB"}, + { 44, "22.0 dB"}, + { 45, "22.5 dB"}, + { 46, "23.0 dB"}, + { 47, "23.5 dB"}, + { 48, "24.0 dB"}, + { 49, "24.5 dB"}, + { 50, "25.0 dB"}, + { 51, "25.5 dB"}, + { 52, "26.0 dB"}, + { 53, "26.5 dB"}, + { 54, "27.0 dB"}, + { 55, "27.5 dB"}, + { 56, "28.0 dB"}, + { 57, "28.5 dB"}, + { 58, "29.0 dB"}, + { 59, "29.5 dB"}, + { 60, "30.0 dB"}, + { 61, "30.5 dB"}, + { 62, "31.0 dB"}, + { 63, "31.5 dB"}, + { 0, NULL } +}; + +static value_string_ext gsm_a_rr_amr_threshold_vals_ext = VALUE_STRING_EXT_INIT(gsm_a_rr_amr_threshold_vals); + +static const value_string gsm_a_rr_amr_hysteresis_vals[] = { + { 0, "0.0 dB"}, + { 1, "0.5 dB"}, + { 2, "1.0 dB"}, + { 3, "1.5 dB"}, + { 4, "2.0 dB"}, + { 5, "2.5 dB"}, + { 6, "3.0 dB"}, + { 7, "3.5 dB"}, + { 8, "4.0 dB"}, + { 9, "4.5 dB"}, + { 10, "5.0 dB"}, + { 11, "5.5 dB"}, + { 12, "6.0 dB"}, + { 13, "6.5 dB"}, + { 14, "7.0 dB"}, + { 15, "7.5 dB"}, + { 0, NULL } +}; + +guint16 +de_rr_multirate_conf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + gint bit_offset, remaining_length, nb_of_params; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_multirate_speech_ver, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_NCSB, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_ICMI, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* The initial codec mode is coded as in 3GPP TS 45.009 */ + proto_tree_add_item(tree, hf_gsm_a_rr_start_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + oct = ( tvb_get_guint8(tvb,curr_offset) &0xe0 ) >> 5; + curr_offset++; + switch ( oct){ + case 1: + /* Adaptive Multirate speech version 1 */ + /* Set of AMR codec modes */ + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + remaining_length = len-2; + break; + case 2: + /* Adaptive Multirate speech version 2 */ + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + remaining_length = len-2; + break; + default: + proto_tree_add_text(tree,tvb,offset,1,"Unknown version"); + proto_tree_add_text(tree,tvb, curr_offset, len-1 ,"Data(Not decoded)"); + remaining_length = 0; + break; + } + + if (remaining_length) + { + bit_offset = (curr_offset<<3) + 2; + nb_of_params = remaining_length - 1; + while (nb_of_params) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_amr_threshold, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_amr_hysteresis, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + nb_of_params -= 1; + } + } + + curr_offset = offset + len; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21b Multislot Allocation + */ +static guint16 +de_rr_mult_all(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_item *item; + guint32 curr_offset; + guint8 oct; + guint8 i; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 1 ,"List of DA:"); + + oct = tvb_get_guint8(tvb, curr_offset); + curr_offset++; + for( i=0;i<7;i++ ) + { + if( (oct>>i) & 1 ) + { + proto_item_append_text(item," DA%d",i+1); + } + } + + if( oct & 0x80 ) /* octet 3a present */ + { + item = proto_tree_add_text(tree,tvb, curr_offset, 1 ,"List of UA:"); + oct = tvb_get_guint8(tvb, curr_offset); + curr_offset++; + for( i=0;i<7;i++ ) + { + if( (oct>>i) & 1 ) + { + proto_item_append_text(item," UA%d",i+1); + } + } + } + + while ( curr_offset < offset + len ) + { + proto_tree_add_item(tree, hf_gsm_a_rr_ma_channel_set, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + } + + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21c NC mode + */ + + /* + * [3] 10.5.2.22 Neighbour Cell Description + */ +static const value_string gsm_a_rr_ext_ind_vals[] = { + { 0, "The information element carries the complete BA"}, + { 1, "The information element carries only a part of the BA"}, + { 0, NULL } +}; +static guint16 +de_rr_neigh_cell_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_ext_ind, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, (curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + + /* + * [3] 10.5.2.22a Neighbour Cell Description 2 + */ +static guint16 +de_rr_neigh_cell_desc2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_multiband_reporting, tvb, (curr_offset<<3)+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, (curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list2(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets + */ + +/* + * [3] 10.5.2.23 P1 Rest Octets + */ +static guint16 +de_rr_p1_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset, value; + gint bit_offset, bit_offset_sav; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = tvb_length_remaining(tvb,offset); + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P1_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P1_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "NLN(PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Group Call Information", "Present", "Not present")) + { /* Group Call Information */ + bit_offset_sav = bit_offset; + bit_offset += 36; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Group Channel Description", "Present", "Not Present")) + { /* Group Channel Description */ + bit_offset += 24; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Hopping case", "Present", "Not Present")) + { /* Hopping case */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "????", "Present", "Not Present")) + { + bit_offset += 64; + } + else + { + value = tvb_get_bits8(tvb,bit_offset,8); + bit_offset += 8 + (value<<3); + } + } + } + proto_tree_add_text(subtree,tvb, bit_offset_sav>>3, (bit_offset-bit_offset_sav)>>3,"Group Call Information: Data(Not decoded)"); + } + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Page Indication 1", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Page Indication 2", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = curr_offset + len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.24 P2 Rest Octets + */ +static guint16 +de_rr_p2_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) + +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = tvb_length_remaining(tvb,offset); + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P2_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P2_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Channel Needed 3", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len ,bit_offset++, "NLN (PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")); + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 3", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 3"); + } + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 1", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 2", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 3", "For GPRS", "For RR connection establishment"); + + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Additions in release 6", "Present", "Not present")) + { /* Additions in release 6 */ + bit_offset += 1; + proto_tree_add_text(subtree, tvb, bit_offset>>3, -1,"Additions in Release 6: Data(Not decoded)"); + } + else + bit_offset += 1; + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = curr_offset + len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.25 P3 Rest Octets + */ +static guint16 +de_rr_p3_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P3_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P3_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Channel Needed 3 & 4", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch4, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "NLN (PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 3", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 3"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 4", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 4"); + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.25a Packet Channel Description C V 3 + */ +static guint16 +de_rr_packet_ch_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb,curr_offset,3, "%s", gsm_rr_elem_strings[DE_RR_PACKET_CH_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_PACKET_CH_DESC]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + /* Channel Type */ + str = "Spare bits (ignored by receiver)"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + /* TN */ + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.25b Dedicated mode or TBF + */ + +static const value_string gsm_a_rr_dedicated_mode_or_tbf_vals[] = { + { 0, "This message assigns a dedicated mode resource"}, + { 1, "This message assigns an uplink TBF or is the second message of two in a two-message assignment of an uplink or downlink TBF"}, + { 2, "Not used"}, + { 3, "This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE"}, + { 4, "Not used"}, + { 5, "This message is the first message of two in a two-message assignment of an uplink TBF"}, + { 6, "Not used"}, + { 7, "This message is the first message of two in a two-message assignment of a downlink TBF to the mobile station identified in the IA Rest Octets IE"}, + { 0, NULL } +}; +static guint16 +de_rr_ded_mod_or_tbf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_DED_MOD_OR_TBF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_DED_MOD_OR_TBF]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_dedicated_mode_or_tbf, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment + */ +/* + * [3] 10.5.2.26 Page Mode + */ + +static const value_string gsm_a_rr_page_mode_vals[] = { + { 0, "Normal paging"}, + { 1, "Extended paging"}, + { 2, "Paging reorganization"}, + { 3, "Same as before"}, + { 0, NULL } +}; +static guint16 +de_rr_page_mode(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_PAGE_MODE].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_PAGE_MODE]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_page_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ +/* + * [3] 10.5.2.27 NCC Permitted + */ +static guint16 +de_rr_ncc_perm(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_NCC_PERM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_NCC_PERM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_ncc_permitted, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.28 Power Command + * + * + * ATC (Access Type Control) (octet 2)Bit 8 + * 0 Sending of Handover access is mandatory + * 1 Sending of Handover access is optional + */ +static const true_false_string gsm_a_rr_pow_cmd_atc_value = { + "Sending of Handover access is optional", + "Sending of Handover access is mandatory" +}; +/* + * The EPC mode field (octet 2) indicates whether the assigned channel(s) + * shall be in enhanced power control (EPC) mode. It is only valid for channels + * on which EPC may be used. It is coded as follows: +*/ +static const true_false_string gsm_a_rr_pow_cmd_epc_value = { + "Channel(s) in EPC mode", + "Channel(s) not in EPC mode" +}; +/* + * FPC_EPC (octet 2) + * The FPC_EPC field (octet 2) has different interpretation depending + * on the channel mode of the assigned channel (s) and the value + * of the EPC mode field. + * If the channel mode is such that fast power control (FPC) may be + * used, the FPC_EPC field indicates whether Fast Measurement + * Reporting and Power Control mechanism is used. + * It is coded as follows: + * Value 0 FPC not in use + * 1 FPC in use + * If the channel mode is such that EPC may be used and the EPC mode + * field indicates that the channel is in EPC mode, the FPC_EPC + * field indicates whether EPC shall be used for uplink power control. + * It is coded as follows: + * Value 0 EPC not in use for uplink power control + * 1 EPC in use for uplink power control + * + */ +static const true_false_string gsm_a_rr_pow_cmd_fpcepc_value = { + "FPC in use/EPC in use for uplink power control", + "FPC not in use/C not in use for uplink power control" +}; + +/* + * Power level (octet 2)The power level field is coded as the binaryRepresentation + * of the "power control level", see 3GPP TS 3GPP TS 45.005. This value shall be used + * by the mobile station According to 3GPP TS 45.008.Range: 0 to 31. + */ + +static guint16 +de_rr_pow_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_POW_CMD].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_POW_CMD]); + + proto_tree_add_item(subtree, hf_gsm_a_b8spare, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*EPC mode */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_epc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*FPC_EPC*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_fpcepc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*POWER LEVEL*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_powlev, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.28a Power Command and access type + */ +static guint16 +de_rr_pow_cmd_and_acc_type(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_POW_CMD_AND_ACC_TYPE].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_POW_CMD_AND_ACC_TYPE]); + + /*ATC */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_atc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*EPC mode */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_epc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*FPC_EPC*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_fpcepc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*POWER LEVEL*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_powlev, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.29 RACH Control Parameters + */ + +static const value_string gsm_a_rr_max_retrans_vals[] = { + { 0, "Maximum 1 retransmission"}, + { 1, "Maximum 2 retransmissions"}, + { 2, "Maximum 4 retransmissions"}, + { 3, "Maximum 7 retransmissions"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_tx_integer_vals[] = { + { 0, "3 slots used to spread transmission"}, + { 1, "4 slots used to spread transmission"}, + { 2, "5 slots used to spread transmission"}, + { 3, "6 slots used to spread transmission"}, + { 4, "7 slots used to spread transmission"}, + { 5, "8 slots used to spread transmission"}, + { 6, "9 slots used to spread transmission"}, + { 7, "10 slots used to spread transmission"}, + { 8, "11 slots used to spread transmission"}, + { 9, "12 slots used to spread transmission"}, + { 10, "14 slots used to spread transmission"}, + { 11, "16 slots used to spread transmission"}, + { 12, "20 slots used to spread transmission"}, + { 13, "25 slots used to spread transmission"}, + { 14, "32 slots used to spread transmission"}, + { 15, "50 slots used to spread transmission"}, + { 0, NULL } +}; +static const value_string gsm_a_rr_cell_barr_access_vals[] = { + { 0, "The cell is not barred"}, + { 1, "The cell is barred"}, + { 0, NULL } +}; +static const value_string gsm_a_rr_re_vals[] = { + { 0, "Call Reestablishment allowed in the cell"}, + { 1, "Call Reestablishment not allowed in the cell"}, + { 0, NULL } +}; + +static guint16 +de_rr_rach_ctrl_param(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_RACH_CTRL_PARAM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_RACH_CTRL_PARAM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_max_retrans, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_tx_integer, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_cell_barr_access, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_re, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_acc, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.30 Request Reference M V 3 + */ +static guint16 reduced_frame_number(guint16 fn) +{ + /* great care needed with signed/unsigned - -1 in unsigned is 0xffff, which mod(26) is not what you think !!! */ + gint16 t2, t3, t; + guint16 frame, t1; + + t1 = (fn >> 11) & 0x1f; + t2 = (fn >> 0) & 0x1f; + t3 = (fn >> 5) & 0x3f; + + t = (t3-t2)%26; + if (t<0) + t += 26; + + frame = 51*(unsigned)t+(unsigned)t3+51*26*t1; + + return frame; +} + +static guint16 +de_rr_req_ref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + guint16 rfn; + guint16 fn; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_REQ_REF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_REQ_REF]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_ra, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + fn = tvb_get_ntohs(tvb,curr_offset); + rfn = reduced_frame_number(fn); + proto_tree_add_item(subtree, hf_gsm_a_rr_T1prim, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_T3, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + curr_offset++; + proto_tree_add_item(subtree, hf_gsm_a_rr_T2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + item = proto_tree_add_uint(subtree, hf_gsm_a_rr_rfn, tvb, curr_offset-2, 2, rfn); + PROTO_ITEM_SET_GENERATED(item); + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.31 + */ +guint16 +de_rr_cause(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_RR_cause, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset++; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.32 SI 1 Rest Octets + */ +static const value_string gsm_a_rr_nch_position_vals[] = { + { 0, "No of blocks = 1 and Number of first block = 0"}, + { 1, "No of blocks = 1 and Number of first block = 1"}, + { 2, "No of blocks = 1 and Number of first block = 2"}, + { 3, "No of blocks = 1 and Number of first block = 3"}, + { 4, "No of blocks = 1 and Number of first block = 4"}, + { 5, "No of blocks = 1 and Number of first block = 5"}, + { 6, "No of blocks = 1 and Number of first block = 6"}, + { 7, "No of blocks = 1 and Number of first block = 0"}, + { 8, "No of blocks = 2 and Number of first block = 1"}, + { 9, "No of blocks = 2 and Number of first block = 2"}, + {10, "No of blocks = 2 and Number of first block = 3"}, + {11, "No of blocks = 2 and Number of first block = 4"}, + {12, "No of blocks = 2 and Number of first block = 5"}, + {13, "No of blocks = 3 and Number of first block = 0"}, + {14, "No of blocks = 3 and Number of first block = 1"}, + {15, "No of blocks = 3 and Number of first block = 2"}, + {16, "No of blocks = 3 and Number of first block = 3"}, + {17, "No of blocks = 3 and Number of first block = 4"}, + {18, "No of blocks = 4 and Number of first block = 0"}, + {19, "No of blocks = 4 and Number of first block = 1"}, + {20, "No of blocks = 4 and Number of first block = 2"}, + {21, "No of blocks = 4 and Number of first block = 3"}, + {22, "No of blocks = 5 and Number of first block = 0"}, + {23, "No of blocks = 5 and Number of first block = 1"}, + {24, "No of blocks = 5 and Number of first block = 2"}, + {25, "No of blocks = 6 and Number of first block = 0"}, + {26, "No of blocks = 6 and Number of first block = 1"}, + {27, "No of blocks = 7 and Number of first block = 0"}, + {28, "Reserved"}, + {29, "Reserved"}, + {30, "Reserved"}, + {31, "Reserved"}, + { 0, NULL } +}; + +static guint16 +de_rr_si1_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + + len = 1; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI1_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI1_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "NCH position", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nch_position, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Band Indicator", "1900", "1800"); + + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.33 SI 2bis Rest Octets + */ + +/* + * [3] 10.5.2.33a SI 2ter Rest Octets + */ +static const value_string gsm_a_rr_qsearch_x_vals[] = { + { 0, "-98 dBm"}, + { 1, "-94 dBm"}, + { 2, "-90 dBm"}, + { 3, "-86 dBm"}, + { 4, "-82 dBm"}, + { 5, "-78 dBm"}, + { 6, "-74 dBm"}, + { 7, "Always"}, + { 8, "-78 dBm"}, + { 9, "-74 dBm"}, + {10, "-70 dBm"}, + {11, "-66 dBm"}, + {12, "-62 dBm"}, + {13, "-58 dBm"}, + {14, "-54 dBm"}, + {15, "Never"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_xdd_qoffset_vals[] = { + { 0, "always select a cell if acceptable"}, + { 1, "-28 dB"}, + { 2, "-24 dB"}, + { 3, "-20 dB"}, + { 4, "-16 dB"}, + { 5, "-12 dB"}, + { 6, "-8 dB"}, + { 7, "-4 dB"}, + { 8, "0 dB"}, + { 9, "4 dB"}, + {10, "8 dB"}, + {11, "12 dB"}, + {12, "16 dB"}, + {13, "20 dB"}, + {14, "24 dB"}, + {15, "28 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_qmin_vals[] = { + { 0, "-20 dB"}, + { 1, "-6 dB"}, + { 2, "-18 dB"}, + { 3, "-8 dB"}, + { 4, "-16 dB"}, + { 5, "-10 dB"}, + { 6, "-14 dB"}, + { 7, "-12 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_qmin_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_rscpmin_vals[] = { + { 0, "-114 dBm"}, + { 1, "-112 dBm"}, + { 2, "-110 dBm"}, + { 3, "-108 dBm"}, + { 4, "-106 dBm"}, + { 5, "-104 dBm"}, + { 6, "-102 dBm"}, + { 7, "-100 dBm"}, + { 8, "-98 dBm"}, + { 9, "-96 dBm"}, + {10, "-94 dBm"}, + {11, "-92 dBm"}, + {12, "-90 dBm"}, + {13, "-88 dBm"}, + {14, "-86 dBm"}, + {15, "-84 dBm"}, + { 0, NULL } +}; + +static guint16 +de_rr_si2ter_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + + len = 4; + curr_offset = offset; + bit_offset = curr_offset<<3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI2TER_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI2TER_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "MP Changemark", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_3g_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_index, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_count, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Current version", "Earlier version"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bandwidth_fdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Current version", "Earlier version"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bandwidth_tdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_i, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-5", "Present", "Not present")) + { /* Additions in release R5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rscpmin, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.33b SI 2quater Rest Octets + */ +static const true_false_string gsm_a_rr_gsm_report_type_value = { + "The MS shall use the Measurement Report message for reporting", + "The MS shall use the Enhanced Measurement Report message for reporting if at least one BSIC is allocated to each BA (list) frequency. Otherwise, the Measurement Report message shall be used" +}; + +static const true_false_string gsm_a_rr_frequency_scrolling_value = { + "Next BSIC in the structure relates to the subsequent frequency in the BA(list)", + "Next BSIC in the structure relates to the same frequency in the BA(list)" +}; + +static const true_false_string gsm_a_rr_rep_priority_value = { + "High reporting priority", + "Normal reporting priority" +}; + +static const true_false_string gsm_a_rr_report_type_value = { + "The MS shall use the PACKET MEASUREMENT REPORT message for reporting", + "The MS shall use the PACKET ENHANCED MEASUREMENT REPORT message for reporting" +}; + +static const true_false_string gsm_a_rr_reporting_rate_value = { + "Reduced reporting rate allowed", + "Normal reporting rate" +}; + +static const true_false_string gsm_a_rr_invalid_bsic_reporting_value = { + "Report on cells with invalid BSIC and allowed NCC part of BSIC is allowed", + "Report on cells with invalid BSIC and allowed NCC part of BSIC is not allowed" +}; + +static const value_string gsm_a_rr_scale_ord_vals[] = { + { 0, "An offset of 0 dB shall be used for the reported RXLEV values"}, + { 1, "An offset of 10 dB shall be used for the reported RXLEV values"}, + { 2, "An automatic offset shall be used for the reported RXLEV values"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_xxx_reporting_offset_vals[] = { + { 0, "Apply an offset of 0 dB to the reported value when prioritising the cells for reporting"}, + { 1, "Apply an offset of 6 dB to the reported value when prioritising the cells for reporting"}, + { 2, "Apply an offset of 12 dB to the reported value when prioritising the cells for reporting"}, + { 3, "Apply an offset of 18 dB to the reported value when prioritising the cells for reporting"}, + { 4, "Apply an offset of 24 dB to the reported value when prioritising the cells for reporting"}, + { 5, "Apply an offset of 30 dB to the reported value when prioritising the cells for reporting"}, + { 6, "Apply an offset of 36 dB to the reported value when prioritising the cells for reporting"}, + { 7, "Apply an offset of 42 dB to the reported value when prioritising the cells for reporting"}, + { 0, NULL } +}; + +/* The TS 45.008 is not that clear, but it should be understood: threshold = minimum reported value + 6 * resolution + * The minimum reported value, resolution is, for the different access technologies: + * GSM: -111 dBm, 1 dBm + * WCDMA/RSCP: -116 dBm, 1 dBm + * WCDMA/EcN0: -24.5 dB, 0.5 dB + * LTE/RSRP: -140 dBm, 1 dB + * LTE/RSRQ: -19.5 dB, 0.5 dB + */ +static const value_string gsm_a_rr_gsm_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -111 dBm"}, + { 1, "Apply priority reporting if the reported value is above -105 dBm"}, + { 2, "Apply priority reporting if the reported value is above -99 dBm"}, + { 3, "Apply priority reporting if the reported value is above -93 dBm"}, + { 4, "Apply priority reporting if the reported value is above -87 dBm"}, + { 5, "Apply priority reporting if the reported value is above -81 dBm"}, + { 6, "Apply priority reporting if the reported value is above -75 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_wcdma_rscp_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -116 dBm"}, + { 1, "Apply priority reporting if the reported value is above -110 dBm"}, + { 2, "Apply priority reporting if the reported value is above -104 dBm"}, + { 3, "Apply priority reporting if the reported value is above -98 dBm"}, + { 4, "Apply priority reporting if the reported value is above -92 dBm"}, + { 5, "Apply priority reporting if the reported value is above -86 dBm"}, + { 6, "Apply priority reporting if the reported value is above -80 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_wcdma_ecn0_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -24.5 dB"}, + { 1, "Apply priority reporting if the reported value is above -21.5 dB"}, + { 2, "Apply priority reporting if the reported value is above -17.5 dB"}, + { 3, "Apply priority reporting if the reported value is above -14.5 dB"}, + { 4, "Apply priority reporting if the reported value is above -11.5 dB"}, + { 5, "Apply priority reporting if the reported value is above -8.5 dB"}, + { 6, "Apply priority reporting if the reported value is above -5.5 dB"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lte_rsrp_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -140 dBm"}, + { 1, "Apply priority reporting if the reported value is above -134 dBm"}, + { 2, "Apply priority reporting if the reported value is above -128 dBm"}, + { 3, "Apply priority reporting if the reported value is above -122 dBm"}, + { 4, "Apply priority reporting if the reported value is above -116 dBm"}, + { 5, "Apply priority reporting if the reported value is above -110 dBm"}, + { 6, "Apply priority reporting if the reported value is above -104 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lte_rsrq_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -19.5 dB"}, + { 1, "Apply priority reporting if the reported value is above -16.5 dB"}, + { 2, "Apply priority reporting if the reported value is above -13.5 dB"}, + { 3, "Apply priority reporting if the reported value is above -10.5 dB"}, + { 4, "Apply priority reporting if the reported value is above -7.5 dB"}, + { 5, "Apply priority reporting if the reported value is above -4.5 dB"}, + { 6, "Apply priority reporting if the reported value is above -1.5 dB"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_network_control_order_vals[] = { + { 0, "NC0"}, + { 1, "NC1"}, + { 2, "NC2"}, + { 3, "NC0"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_nc_non_drx_period_vals[] = { + { 0, "No non-DRX mode after a measurement report has been sent"}, + { 1, "0,24 s"}, + { 2, "0,48 s"}, + { 3, "0.72 s"}, + { 4, "0.96 s"}, + { 5, "1.20 s"}, + { 6, "1.44 s"}, + { 7, "1.92 s"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_nc_reporting_period_x_vals[] = { + { 0, "0.48 s"}, + { 1, "0.96 s"}, + { 2, "1.92 s"}, + { 3, "3.84 s"}, + { 4, "7.68 s"}, + { 5, "15.36 s"}, + { 6, "30.72 s"}, + { 7, "61.44 s"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_qsearch_c_initial_value = { + "Always", + "use Qsearch I" +}; + +static const true_false_string gsm_a_rr_fdd_rep_quant_value = { + "Ec/No", + "RSCP" +}; + +static const true_false_string gsm_a_rr_3g_search_prio_value = { + "3G cells may be searched when BSIC decoding is required", + "3G cells may not be searched when BSIC decoding is required" +}; + +static const true_false_string gsm_a_rr_3g_ccn_active_value = { + "CCN towards 3G cells is enabled in the cell", + "CCN towards 3G cells is disabled in the cell" +}; + +static gint +de_rr_si2quater_meas_info_utran_fdd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, idx; + gint xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_fdd, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UMTS FDD Neighbour Cells", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "the FDD_CELL_INFORMATION parameter value '0000000000' ", "is a member of the set", "is not a member of the set"); + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Nr of FDD Cells : %d", idx); + curr_bit_offset += 5; + idx = convert_n_to_p[idx]; + item2 = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (idx>>3)+1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Scrambling Code: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity: %d", 0); + } + if (idx) + { + wsize = 10; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, curr_bit_offset, wsize, ENC_BIG_ENDIAN); + curr_bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + curr_bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 1024); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Scrambling Code: %d", xdd_cell_info & 0x01FF); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity: %d", (xdd_cell_info >> 9) & 0x01); + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_si2quater_meas_info_utran_tdd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, idx; + gint xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_tdd, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UMTS TDD Neighbour Cells", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "the FDD_CELL_INFORMATION parameter value '0000000000' ", "is a member of the set", "is not a member of the set"); + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Nr of TDD Cells : %d", idx); + curr_bit_offset += 5; + idx = convert_n_to_q[idx]; + item2 = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (idx>>3)+1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Cell Parameter: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Sync Case TSTD: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity TDD: %d", 0); + } + if (idx) + { + wsize = 9; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, curr_bit_offset, wsize, ENC_BIG_ENDIAN); + curr_bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + curr_bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 512); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Cell Parameter: %d", xdd_cell_info & 0x07F); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Sync Case TSTD: %d", (xdd_cell_info >> 7) & 0x01); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity TDD: %d", (xdd_cell_info >> 8) & 0x01); + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_rtd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start RTD", "Present", "Not Present")) + { + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start RTD: %d", idx); + curr_bit_offset += 5; + } + else + idx = 0; + + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD", "Not Present", "Present"); + while (value == 0) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "RTD index %d: %d TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits8(tvb,curr_bit_offset,6)); + curr_bit_offset += 6; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD", "Not Present", "Present"); + } + + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD struct", "Not Present", "Present"); + while(value == 0) + { /* all other frequencies */ + idx += 1; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD", "Not Present", "Present"); + while (value == 0) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "RTD index %d: %d TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits8(tvb,curr_bit_offset,6)); + curr_bit_offset += 6; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD", "Not Present", "Present"); + } + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD struct", "Not Present", "Present"); + } + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD12", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD12", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start RTD", "Present", "Not Present")) + { + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start RTD: %d", idx); + curr_bit_offset += 5; + } + else + idx = 0; + + while (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD", "Not Present", "Present")) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 2, "RTD index %d: %d/64 TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN)); + curr_bit_offset += 12; + } + + while(0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD Frequency", "Not Present", "Present")) + { /* all other frequencies */ + idx += 1; + while (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD", "Not Present", "Present")) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 2, "RTD index %d: %d/64 TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN)); + curr_bit_offset += 12; + } + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_bsic_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start BSIC", "Present", "Not Present")) + { + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start BSIC: %d", tvb_get_bits8(tvb,curr_bit_offset,5)); + curr_bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + idx = tvb_get_bits8(tvb,curr_bit_offset,7); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Number Remaining BSIC: %d", idx); + curr_bit_offset += 7; + while (idx) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_frequency_scrolling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + idx -= 1; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_report_priority_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + idx = tvb_get_bits8(tvb,curr_bit_offset,7); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Number Cells: %d", idx); + curr_bit_offset += 7; + while (idx) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rep_priority, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + idx -= 1; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_meas_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (id == DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_report_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_rate, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset +=1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_invalid_bsic_reporting, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset +=1; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Multiband Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_multiband_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Serving Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_serving_band_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_scale_ord, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "900 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_900_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_900_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "1800 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1800_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1800_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "400 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_400_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_400_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "1900 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1900_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1900_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "850 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_850_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_850_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_add_meas_param_desc2(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "FDD Reporting Threshold2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +/* Additions in Rel-8 */ +static const true_false_string priority_utran_start = { + "This is the first instance of the message", + "This is not the first instance of the message" +}; + +static const true_false_string priority_utran_stop = { + "This is the last instance of the message", + "This is not the last instance of the message" +}; + +static const true_false_string eutran_ccn_active = { + "CCN towards E-UTRAN cells is enabled in the cell", + "The broadcast E-UTRAN_CCN_ACTIVE parameter shall apply if applicable. Otherwise, CCN towards E-UTRAN cells is disabled in the cell" +}; + +static const true_false_string eutran_rep_quant = { + "RSRQ", + "RSRP" +}; + +static const value_string gsm_a_rr_pcid_psc_pattern_length[] = { + { 0, "1"}, + { 1, "2"}, + { 2, "3"}, + { 3, "4"}, + { 4, "5"}, + { 5, "6"}, + { 6, "7"}, + { 7, "8"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_eutran_measurement_bandwidth[] = { + { 0, "NRB = 6"}, + { 1, "NRB = 15"}, + { 2, "NRB = 25"}, + { 3, "NRB = 50"}, + { 4, "NRB = 75"}, + { 5, "NRB = 100"}, + { 6, "Reserved for future use"}, + { 7, "Reserved for future use"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_thresh_gsm_low[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "Always allowed"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_thresh_priority_search[] = { + { 0, "-98 dBm"}, + { 1, "-95 dBm"}, + { 2, "-92 dBm"}, + { 3, "-89 dBm"}, + { 4, "-86 dBm"}, + { 5, "-83 dBm"}, + { 6, "-80 dBm"}, + { 7, "-77 dBm"}, + { 8, "-74 dBm"}, + { 9, "-71 dBm"}, + {10, "-68 dBm"}, + {11, "-65 dBm"}, + {12, "-62 dBm"}, + {13, "-59 dBm"}, + {14, "-56 dBm"}, + {15, "Always search"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_utran_qrxlevmin[] = { + { 0, "-119 dBm"}, + { 1, "-117 dBm"}, + { 2, "-115 dBm"}, + { 3, "-113 dBm"}, + { 4, "-111 dBm"}, + { 5, "-109 dBm"}, + { 6, "-107 dBm"}, + { 7, "-105 dBm"}, + { 8, "-103 dBm"}, + { 9, "-101 dBm"}, + {10, "-99 dBm"}, + {11, "-97 dBm"}, + {12, "-95 dBm"}, + {13, "-93 dBm"}, + {14, "-91 dBm"}, + {15, "-89 dBm"}, + {16, "-87 dBm"}, + {17, "-85 dBm"}, + {18, "-83 dBm"}, + {19, "-81 dBm"}, + {20, "-79 dBm"}, + {21, "-77 dBm"}, + {22, "-75 dBm"}, + {23, "-73 dBm"}, + {24, "-71 dBm"}, + {25, "-69 dBm"}, + {26, "-67 dBm"}, + {27, "-65 dBm"}, + {28, "-63 dBm"}, + {29, "-61 dBm"}, + {30, "-59 dBm"}, + {31, "-57 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_thresh_utran_eutran_high_low[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "30 dB"}, + {16, "32 dB"}, + {17, "34 dB"}, + {18, "36 dB"}, + {19, "38 dB"}, + {20, "40 dB"}, + {21, "42 dB"}, + {22, "44 dB"}, + {23, "46 dB"}, + {24, "48 dB"}, + {25, "50 dB"}, + {26, "52 dB"}, + {27, "54 dB"}, + {28, "56 dB"}, + {29, "58 dB"}, + {30, "60 dB"}, + {31, "62 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_eutran_qrxlevmin[] = { + { 0, "-140 dBm"}, + { 1, "-138 dBm"}, + { 2, "-136 dBm"}, + { 3, "-134 dBm"}, + { 4, "-132 dBm"}, + { 5, "-130 dBm"}, + { 6, "-128 dBm"}, + { 7, "-126 dBm"}, + { 8, "-124 dBm"}, + { 9, "-122 dBm"}, + {10, "-120 dBm"}, + {11, "-118 dBm"}, + {12, "-116 dBm"}, + {13, "-114 dBm"}, + {14, "-112 dBm"}, + {15, "-110 dBm"}, + {16, "-108 dBm"}, + {17, "-106 dBm"}, + {18, "-104 dBm"}, + {19, "-102 dBm"}, + {20, "-100 dBm"}, + {21, "-98 dBm"}, + {22, "-96 dBm"}, + {23, "-94 dBm"}, + {24, "-92 dBm"}, + {25, "-90 dBm"}, + {26, "-88 dBm"}, + {27, "-86 dBm"}, + {28, "-84 dBm"}, + {29, "-82 dBm"}, + {30, "-80 dBm"}, + {31, "-78 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_priority_param_h_prio[] = { + { 0, "disabled"}, + { 1, "5 dB"}, + { 2, "4 dB"}, + { 3, "3 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_priority_param_t_reselection[] = { + { 0, "5 s"}, + { 1, "10 s"}, + { 2, "15 s"}, + { 3, "20 s"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_qsearch_c_eutran_initial[] = { + { 0, "search if signal is below -98 dBm"}, + { 1, "search if signal is below -94 dBm"}, + { 2, "search if signal is below -90 dBm"}, + { 3, "search if signal is below -86 dBm"}, + { 4, "search if signal is below -82 dBm"}, + { 5, "search if signal is below -78 dBm"}, + { 6, "search if signal is below -74 dBm"}, + { 7, "always search"}, + { 8, "search is signal is above -78 dBm"}, + { 9, "search is signal is above -74 dBm"}, + {10, "search is signal is above -70 dBm"}, + {11, "search is signal is above -66 dBm"}, + {12, "search is signal is above -62 dBm"}, + {13, "search is signal is above -58 dBm"}, + {14, "search is signal is above -54 dBm"}, + {15, "never search"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pcid_pattern_sense = { + "The group of identified cells are the one not belonging to the PCID_BITMAP_GROUP", + "The group of identified cells are the one identified by the PCID_BITMAP_GROUP" +}; + +static const true_false_string measurement_control_utran = { + "Frequency-specific search enabled: use Qsearch_C_E-UTRAN if received, otherwise use Qsearch_C_EUTRAN_Initial", + "Frequency-specific search enabled: Never search" +}; + +static const true_false_string measurement_control_eutran = { + "Frequency-specific search enabled: use Qsearch_C if received, otherwise use Qsearch_C_Initial", + "Frequency-specific search enabled: Never search" +}; + +static gint +de_rr_3g_priority_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Default UTRAN Priority Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + /* Repeated UTRAN Priority Parameters */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Priority Parameters", "Present", "Not Present")) + { + proto_tree *subtree_rep_utran_prio; + proto_item *item_rep_utran_prio; + gint rep_utran_prio_bit_offset = curr_bit_offset; + + item_rep_utran_prio = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM].strptr); + subtree_rep_utran_prio = proto_item_add_subtree(item_rep_utran_prio, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_thresh_utran_high, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Threshold UTRAN Low", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_thresh_utran_low, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Qrxlev Min", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + proto_item_set_len(item_rep_utran_prio,((curr_bit_offset-rep_utran_prio_bit_offset)>>3)+1); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_neighbour_cells(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Neighbour Cells Struct", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Bandwidth", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_bandwidth, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_thresh_eutran_high, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Threshold E-UTRAN Low", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_thresh_eutran_low, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Qrxlev Min", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_neighbour_cells_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated EARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Bandwidth ", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_bandwidth, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_pcid(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + gint curr_bit_offset = bit_offset; + proto_item *item; + + while (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "Repeated PCID", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "PCID Bitmap Group", "Present", "Not Present")) + { + gint i; + guint8 bitmap = tvb_get_bits8(tvb,curr_bit_offset,6); + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_bitmap_group, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (bitmap > 0) + { + proto_item_append_text(item, ": Cells IDs addressed by the bitmap:"); + } + for (i = 0; i < 6; i++) + { + if ((1 << i) & bitmap) + { + if ( i != 0) + { + proto_item_append_text(item, ","); + } + proto_item_append_text(item, " %d to %d",i*84, (i+1)*84 - 1); + } + } + curr_bit_offset += 6; + } + while (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "PCID Pattern", "Present", "Not Present")) + { + gint pcid_pattern_length; + gint pcid_pattern; + gint pattern_lower_bound, pattern_upper_bound; + gint i; + + pcid_pattern_length = tvb_get_bits8(tvb,curr_bit_offset,3) + 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_pattern_length, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + pcid_pattern = tvb_get_bits8(tvb,curr_bit_offset, pcid_pattern_length); + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s = PCID_Pattern: %d", + decode_bits_in_field(curr_bit_offset,pcid_pattern_length, pcid_pattern), + pcid_pattern); + + pattern_lower_bound = pcid_pattern << (9 - pcid_pattern_length); + pattern_upper_bound = pattern_lower_bound; + for (i = 0; i < (9-pcid_pattern_length); i++) + { + pattern_upper_bound |= 1 << i; + } + proto_item_append_text(item, ": Cells IDs addressed by the pattern: %d to %d", pattern_lower_bound, pattern_upper_bound); + + curr_bit_offset += pcid_pattern_length; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_pattern_sense, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_not_allowed_cells(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS]); + + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_frequency_index, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_pcid_to_ta_mapping(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeating PCID group", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_measurement_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + gint curr_bit_offset; + proto_item *item; + guint8 rep_quant; + + curr_bit_offset = bit_offset; + + /* E-UTRAN Measurement Parameters Description */ + proto_tree_add_bits_item(tree, hf_gsm_a_rr_qsearch_c_eutran_initial, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_rep_quant, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + rep_quant = gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN Reporting Quantity", "RSRQ", "RSRP"); + curr_bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_multirat_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + + if (!gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "Reporting", "3 bit", "6 bit")) + { + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD FDD 3 bit Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN FDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD TDD 3 bit Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + + curr_bit_offset += 3; + } + } + } + else + { + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN FDD Reporing Threshold", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_measurement_report_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + else + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + curr_bit_offset += 6; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporing Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_measurement_report_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + else + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + curr_bit_offset += 6; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + } + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_reporting_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dB step)", 2 + tvb_get_bits8(tvb,curr_bit_offset,1)); + } + else + { + proto_item_append_text(item, " (%d dB step)", 1 + tvb_get_bits8(tvb,curr_bit_offset,1)); + } + curr_bit_offset += 1; + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 rep_quant = 0; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_ccn_active, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* E-UTRAN Measurement Parameters Description */ + curr_bit_offset += de_rr_eutran_measurement_param_desc(tvb, subtree, curr_bit_offset); + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "GPRS E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* GPRS E-UTRAN Measurement Parameters Description */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_qsearch_p_eutran, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_rep_quant, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + rep_quant = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Reporting Quantity", "RSRQ", "RSRP"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_multirat_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporitng Threshold 2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporitng Threshold 2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + } + + /* Repeated E-UTRAN Neighbour Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Neighbour Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_neighbour_cells(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Not Allowed Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Not Allowed Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_not_allowed_cells(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN PCID to TA mapping */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN PCID to TA mapping", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_pcid_to_ta_mapping(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_param_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* E-UTRAN Measurement Parameters Description */ + curr_bit_offset += de_rr_eutran_measurement_param_desc(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Neighbour Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Neighbour Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_neighbour_cells_mi(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Not Allowed Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Not Allowed Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_not_allowed_cells(tvb, subtree, curr_bit_offset); + } + + /* Measurement Control Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Control Parameters Description", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Default Measurement Control E-UTRAN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_default_measurement_control_eutran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Measurement Control Parameters", "Present", "Not Present")) + { + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_frequency_index, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_control_eutran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_priority_and_eutran_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC]); + + + /* Serving Cell Priority Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Serving Cell Priority Parameters Description", "Present", "Not Present")) + { + proto_tree *subtree_serv; + proto_item *item_serv; + gint serv_bit_offset = curr_bit_offset; + + item_serv = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, ((curr_bit_offset+15)>>3)-(curr_bit_offset>>3) + 1 , "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC].strptr); + subtree_serv = proto_item_add_subtree(item_serv, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_geran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_h_prio, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_t_reselection, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_item_set_len(item_serv,((curr_bit_offset-serv_bit_offset)>>3)+1); + } + + /* 3G Priority Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Priority Parameters Description", "Present", "Not Present")) + { + curr_bit_offset += de_rr_3g_priority_param_desc(tvb, subtree, curr_bit_offset); + } + + /* E-UTRAN Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Parameters Description", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_param_desc(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_csg_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeated CSG_PSC_SPLIT struct", "Present", "Not Present")) + { + /* CSG_PSC_SPLIT struct */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PSC", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_psc, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PSC Pattern", "Present", "Not Present")) + { + gint psc_pattern_length; + gint psc_pattern; + + psc_pattern_length = tvb_get_bits8(tvb,curr_bit_offset,3) + 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_psc_pattern_length, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + psc_pattern = tvb_get_bits8(tvb,curr_bit_offset, psc_pattern_length); + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s = PSC_Pattern: %d", + decode_bits_in_field(curr_bit_offset,psc_pattern_length, psc_pattern), + psc_pattern); + + curr_bit_offset += psc_pattern_length; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_psc_pattern_sense, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + while(gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "repeated CSG FDD UARFCN", "Present", "Not Present")) + { + /* CSG_FDD_UARFCN */ + if (!gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Technology", "TDD", "FDD")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_csg_fdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_csg_tdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_csg_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated PCID group", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated EARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_csg_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_csg_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated CSG PCI Split", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_utran_measurement_control_param_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_supplementary_param_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + /* 3G Measurement Control Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Measurement Control Parameters Description", "Present", "Not Present")) + { + /* 3G Measurement Control Parameters Description struct */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Measurement Control Parameters Description struct", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Measurement Control Parameters struct", "Present", "Not Present")) + { + curr_bit_offset += de_rr_utran_measurement_control_param_mi(tvb, subtree, curr_bit_offset); + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static guint16 +de_rr_si2quater_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2, *subtree3; + proto_item *item, *item2, *item3; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, idx; + guint8 value; + guint8 tvb_len = tvb_length(tvb); + len = 20; + curr_offset = offset; + bit_offset = curr_offset<<3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI2QUATER_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI2QUATER_REST_OCT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_index, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_count, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Measurement Parameters Description", "Present", "Not Present")) + { /* Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_gsm_report_type, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_serving_band_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Real Time Difference Description", "Present", "Not Present")) + { /* GPRS Real Time Difference Description */ + bit_offset += de_rr_rtd_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_RTD_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS BSIC Description", "Present", "Not Present")) + { /* GPRS BSIC Description */ + bit_offset += de_rr_bsic_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_BSIC_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Report Priority Description", "Present", "Not Present")) + { /* GPRS Report Priority Description */ + bit_offset += de_rr_report_priority_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_REPORT_PRIO_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Measurement Parameters Description", "Present", "Not Present")) + { /* GPRS Measurement Parameters Description */ + bit_offset += de_rr_meas_param_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "NC Measurement Parameters", "Present", "Not Present")) + { /* NC Measurement Parameters */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_NC_MEAS_PARAM].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_NC_MEAS_PARAM]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_network_control_order, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "NC Periods", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_non_drx_period, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_reporting_period_i, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_reporting_period_t, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI 2quater Extension Information", "Present", "Not Present")) + { /* SI 2quater Extension Information */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI2Q_EXT_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI2Q_EXT_INFO]); + idx = tvb_get_bits8(tvb,bit_offset,8); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 1, "Extension Length: %d", idx); + bit_offset += 8; + proto_item_set_len(item2,((bit_offset+idx+1-bit_offset_sav)>>3)+1); + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "CCN Support Description", "Present", "Not Present")) + { /* CCN Support Description */ + bit_offset_sav = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_CCN_SUPPORT_DESC].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_CCN_SUPPORT_DESC]); + value = tvb_get_bits8(tvb,bit_offset,7); + proto_tree_add_text(subtree3,tvb, bit_offset>>3, 1, "Number Cells: %d", value); + bit_offset += 7; + idx -= 7; + item2 = proto_tree_add_text(subtree3,tvb, bit_offset>>3, (value>>3)+1, "CCN Supported: "); + while (value) + { + proto_item_append_text(item2,"%d",tvb_get_bits8(tvb,bit_offset,1)); + bit_offset += 1; + value -= 1; + idx -= 1; + } + proto_item_set_len(item3,((bit_offset+-bit_offset_sav)>>3)+1); + } + bit_offset += idx; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Neighbour Cell Description", "Present", "Not Present")) + { /* 3G Neighbour Cell Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Index Start 3G", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_index_start_3g, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Absolute Index Start EMR", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_absolute_index_start_emr, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_fdd_desc(tvb, subtree2, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_tdd_desc(tvb, subtree2, bit_offset); + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_i, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_c_initial, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "FDD Information", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Information", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS 3G Measurement Parameters Description", "Present", "Not Present")) + { /* GPRS 3G Measurement Parameters Description */ + guint8 reporting_quant = 0; + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_p, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_3g_search_prio, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + reporting_quant = gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Reporting Quantity", "Ec/No", "RSCP"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "FDD Reporting Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Reporting Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-5", "Present", "Not present")) + { /* Additions in Rel-5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rscpmin, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description 2", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description 2 */ + bit_offset += de_rr_3g_add_meas_param_desc2(tvb, subtree, bit_offset); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-6", "Present", "Not present")) + { /* Additions in Rel-6 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ccn_active, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-7", "Present", "Not present")) + { /* Additions in Rel-7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "700 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_700_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_700_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "810 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_810_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_810_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + /* Additions in Rel-8 */ + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-8", "Present", "Not present")) + { /* Additions in Rel-8 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Supplementary Parameters Description ", "Present", "Not Present")) + { + bit_offset += de_rr_priority_and_eutran_param_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G CSG Description ", "Present", "Not Present")) + { + bit_offset += de_rr_3g_csg_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "EUTRAN CSG Description ", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_csg_desc(tvb, subtree, bit_offset); + } + } + } + } + } + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.34 SI 3 Rest Octets + */ +static const value_string gsm_a_rr_temporary_offset_vals[] = { + { 0, "0 dB"}, + { 1, "10 dB"}, + { 2, "20 dB"}, + { 3, "30 dB"}, + { 4, "40 dB"}, + { 5, "50 dB"}, + { 6, "60 dB"}, + { 7, "infinity"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cell_reselect_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "30 dB"}, + {16, "32 dB"}, + {17, "34 dB"}, + {18, "36 dB"}, + {19, "38 dB"}, + {20, "40 dB"}, + {21, "42 dB"}, + {22, "44 dB"}, + {23, "46 dB"}, + {24, "48 dB"}, + {25, "50 dB"}, + {26, "52 dB"}, + {27, "54 dB"}, + {28, "56 dB"}, + {29, "58 dB"}, + {30, "60 dB"}, + {31, "62 dB"}, + {32, "64 dB"}, + {33, "66 dB"}, + {34, "68 dB"}, + {35, "70 dB"}, + {36, "72 dB"}, + {37, "74 dB"}, + {38, "76 dB"}, + {39, "78 dB"}, + {40, "80 dB"}, + {41, "82 dB"}, + {42, "84 dB"}, + {43, "86 dB"}, + {44, "88 dB"}, + {45, "90 dB"}, + {46, "92 dB"}, + {47, "94 dB"}, + {48, "96 dB"}, + {49, "98 dB"}, + {50, "100 dB"}, + {51, "102 dB"}, + {52, "104 dB"}, + {53, "106 dB"}, + {54, "108 dB"}, + {55, "110 dB"}, + {56, "112 dB"}, + {57, "114 dB"}, + {58, "116 dB"}, + {59, "118 dB"}, + {60, "120 dB"}, + {61, "122 dB"}, + {62, "124 dB"}, + {63, "126 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_penalty_time_vals[] = { + { 0, "20 s"}, + { 1, "40 s"}, + { 2, "60 s"}, + { 3, "80 s"}, + { 4, "100 s"}, + { 5, "120 s"}, + { 6, "140 s"}, + { 7, "160 s"}, + { 8, "180 s"}, + { 9, "200 s"}, + {10, "220 s"}, + {11, "240 s"}, + {12, "260 s"}, + {13, "280 s"}, + {14, "300 s"}, + {15, "320 s"}, + {16, "340 s"}, + {17, "360 s"}, + {18, "380 s"}, + {19, "400 s"}, + {20, "420 s"}, + {21, "440 s"}, + {22, "460 s"}, + {23, "480 s"}, + {24, "500 s"}, + {25, "520 s"}, + {26, "540 s"}, + {27, "560 s"}, + {28, "580 s"}, + {29, "600 s"}, + {30, "620 s"}, + {31, "Cell Reselect Offset is subtracted from C2 and Temporary Offset is ignored"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_opt_sel_param(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + if (gsm_rr_csn_HL_flag(tvb,tree,0,curr_bit_offset++,"Selection Parameters", "Present", "Not present")) + { /* Selection Parameters */ + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cell_reselect_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_temporary_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_penalty_time, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + } + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_si13_position_vals[] = { + { 0, "SYSTEM INFORMATION TYPE 13 message is sent on BCCH Norm"}, + { 1, "SYSTEM INFORMATION TYPE 13 message is sent on BCCH Ext"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_gprs_indicator(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_INDICATOR].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_INDICATOR]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_ra_colour, tvb, curr_bit_offset, 3, FALSE); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13_position, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_power_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "8 dB"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_si2quater_position_value = { + "SYSTEM INFORMATION TYPE 2 quater message is sent on BCCH Ext", + "SYSTEM INFORMATION TYPE 2 quater message is sent on BCCH Norm" +}; + +static const true_false_string gsm_a_rr_si13alt_position_value = { + "If Iu mode is supported in the cell, SYSTEM INFORMATION TYPE 13alt message is sent on BCCH Ext", + "If Iu mode is supported in the cell, SYSTEM INFORMATION TYPE 13alt message is sent on BCCH Norm" +}; + +static guint16 +de_rr_si3_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + gboolean gprs_indicator; + + len = 4; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI3_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI3_REST_OCT]); + + bit_offset += de_rr_rest_oct_opt_sel_param(tvb, subtree, bit_offset); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Optional Power Offset", "Present", "Not present")) + { /* Optional Power Offset */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_power_offset, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SYSTEM INFORMATION TYPE 2ter", "Available", "Not Available"); + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Early Classmark Sending", "Is allowed", "Is forbidden"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Scheduling if and where", "Present", "Not present")) + { /* Scheduling if and where */ + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Where: %d",tvb_get_bits8(tvb,bit_offset,3)); + bit_offset += 3; + } + gprs_indicator = gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "GPRS Indicator", "Present", "Not present"); + if (gprs_indicator) + { /* GPRS indicator */ + bit_offset += de_rr_rest_oct_gprs_indicator(tvb, subtree, bit_offset); + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "3G Early Classmark Sending Restriction", + "The sending of UTRAN,CDMA2000 and GERAN IU MODE CLASSMARK CHANGE messages are controlled by the Early Classmark Sending Control parameter", + "Neither UTRAN, CDMA2000 nor GERAN IU MODE CLASSMARK CHANGE message shall be sent with the Early classmark sending"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SI2quater Indicator", "Present", "Not present")) + { /* SI2quater Indicator */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gprs_indicator == FALSE) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13alt_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.32 SI 4 Rest Octets + */ +static const value_string gsm_a_rr_prio_thr_vals[] = { + { 0, "0 dB"}, + { 1, "6 dB"}, + { 2, "12 dB"}, + { 3, "18 dB"}, + { 4, "24 dB"}, + { 5, "30 dB"}, + { 6, "36 dB"}, + { 7, "Infinite"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lsa_offset_vals[] = { + { 0, "0 dB"}, + { 1, "4 dB"}, + { 2, "8 dB"}, + { 3, "16 dB"}, + { 4, "24 dB"}, + { 5, "32 dB"}, + { 6, "48 dB"}, + { 7, "64 dB"}, + { 0, NULL } +}; + +static guint16 +de_rr_si4_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2, *subtree3; + proto_item *item, *item2, *item3; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, bit_offset_sav2; + guint value; + + guint8 tvb_len = tvb_length(tvb); + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI4_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI4_REST_OCT]); + + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI4_REST_OCTETS_O].strptr); + + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI4_REST_OCTETS_O]); + + bit_offset += de_rr_rest_oct_opt_sel_param(tvb, subtree2, bit_offset); + + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "Optional Power Offset", "Present", "Not present")) + { /* Optional Power Offset */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_power_offset, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "GPRS Indicator", "Present", "Not present")) + { + bit_offset += de_rr_rest_oct_gprs_indicator(tvb, subtree2, bit_offset); + } + proto_item_set_len(item2,(bit_offset>>3)+1-curr_offset); + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "SI4 Rest Octets_S", "Present", "Not present")) + { /* SI4 Rest Octets_S */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI4_REST_OCTETS_S].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI4_REST_OCTETS_S]); + if (gsm_rr_csn_HL_flag(tvb,subtree2,curr_offset + len,bit_offset++,"LSA Parameters", "Present", "Not present")) + { /* LSA Parameters */ + bit_offset_sav2 = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_LSA_PARAMETERS].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_LSA_PARAMETERS]); + proto_tree_add_bits_item(subtree3, hf_gsm_a_rr_prio_thr, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree3, hf_gsm_a_rr_lsa_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb,subtree3, bit_offset++,"MCC/MNC", "Present", "Not Present")) + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 2, "MCC: %d", tvb_get_bits16(tvb,bit_offset,12,ENC_BIG_ENDIAN)); + bit_offset += 12; + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 2, "MNC: %d", tvb_get_bits16(tvb,bit_offset,12,ENC_BIG_ENDIAN)); + bit_offset += 12; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav2)>>3)+1); + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "Cell Identity", "Present", "Not present")) + { /* Cell Identity */ + value = tvb_get_bits16(tvb, bit_offset, 16, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree2, hf_gsm_a_bssmap_cell_ci, tvb, bit_offset>>3, 2, value); + bit_offset += 16; + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "LSA ID information", "Present", "Not present")) + { /* LSA ID information */ + bit_offset_sav2 = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, len, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_LSA_ID_INFO].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_LSA_ID_INFO]); + do + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Type:", "Short LSA ID", "LSA ID")) + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 3, "Short LSA ID: %d",tvb_get_bits16(tvb,bit_offset,10,ENC_BIG_ENDIAN)); + bit_offset += 10; + } + else + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 3, "LSA ID: %d",tvb_get_bits32(tvb,bit_offset,24,ENC_BIG_ENDIAN)); + bit_offset += 24; + } + } while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Additional LSA ID", "Present", "Not Present")); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "CBQ3", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI3 alt position", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13alt_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + else + { /* Break indicator */ + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Break Indicator", + "Additional parameters \"SI4 Rest Octets_S\" are sent in SYSTEM INFORMATION TYPE 7 and 8", + "Additional parameters \"SI4 Rest Octets_S\" are not sent in SYSTEM INFORMATION TYPE 7 and 8"); + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.35a SI 6 Rest Octets + */ +static const true_false_string gsm_a_rr_paging_channel_restructuring_value = { + "Paging channel is restructured", + "Paging channel is not restructured" +}; + +static const true_false_string gsm_a_rr_vbs_vgcs_inband_notifications_value = { + "The mobile shall be notified on incoming high priority VBS/VGCS calls through NOTIFICATION/FACCH, the mobile need not to inspect the NCH", + "The network does not provide notification on FACCH so that the mobile should inspect the NCH for notifications" +}; + +static const true_false_string gsm_a_rr_vbs_vgcs_inband_pagings_value = { + "The mobile shall be notified on incoming high priority point-to-point calls through NOTIFICATION/FACCH, the mobile need not to inspect the PCH", + "The network does not provide paging information on FACCH so that the mobile should inspect the PCH for pagings" +}; + +static const value_string gsm_a_rr_max_lapdm_vals[] = { + { 0, "Any message segmented in up to 5 LAPDm frames"}, + { 1, "Any message segmented in up to 6 LAPDm frames"}, + { 2, "Any message segmented in up to 7 LAPDm frames"}, + { 3, "Any message segmented in up to 8 LAPDm frames"}, + { 4, "Any message segmented in up to 9 LAPDm frames"}, + { 5, "Any message segmented in up to 10 LAPDm frames"}, + { 6, "Any message segmented in up to 11 LAPDm frames"}, + { 7, "Any message segmented in up to 12 LAPDm frames"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_dedicated_mode_mbms_notification_support_value = { + "The cell supports the Dedicated Mode MBMS Notification procedures", + "The cell does not support the Dedicated Mode MBMS Notification procedures" +}; + +static const true_false_string gsm_a_rr_mnci_support_value = { + "The cell supports the distribution of MBMS NEIGHBOURING CELL INFORMATION messages", + "The cell does not support the distribution of MBMS NEIGHBOURING CELL INFORMATION messages" +}; + +static guint16 +de_rr_si6_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + guint8 value; + + len = 7; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI6_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI6_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "PCH and NCH Info", "Present", "Not present")) + { /* PCH and NCH Info */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PCH_AND_NCH_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PCH_AND_NCH_INFO]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_paging_channel_restructuring, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nln_sacch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Call Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_call_prio, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nln_status_sacch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "VBS/VGCS options", "Present", "Not present")) + { /* VBS/VGCS options */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb,bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_vbs_vgcs_inband_notifications, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_vbs_vgcs_inband_pagings, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + value = gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "DTM", "Supported in Serving cell", "Not Supported in Serving cell"); + if (value == TRUE) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rac, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_max_lapdm, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Band Indicator", "1900", "1800"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "GPRS MS PWR MAX CCCH", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_ms_txpwr_max_ccch, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "MBMS Procedures", "Supported", "Not supported")) + { /* MBMS Procedures */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dedicated_mode_mbms_notification_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mnci_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-7", "Present", "Not present")) + { /* Additions in Release 7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "AMR Config", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_amr_config, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + +/* + * [3] 10.5.2.37b SI 13 Rest Octets + */ +static const value_string gsm_a_rr_si_change_field_vals[] = { + { 0, "Update of unspecified SI message or SI messages"}, + { 1, "Update of SI1 message"}, + { 2, "Update of SI2, SI2 bis or SI2 ter message or any instance of SI2quater messages"}, + { 3, "Update of SI3, SI4, SI7, SI8, SI16 or SI17 message"}, + { 4, "Update of SI9 message"}, + { 5, "Update of SI18 or SI20 message"}, + { 6, "Update of SI19 message"}, + { 7, "Update of SI15 message"}, + { 8, "Update of SI2n message"}, + { 9, "Update of unknown SI message type"}, + { 10, "Update of unknown SI message type"}, + { 11, "Update of unknown SI message type"}, + { 12, "Update of unknown SI message type"}, + { 13, "Update of unknown SI message type"}, + { 14, "Update of unknown SI message type"}, + { 15, "Update of unknown SI message type"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_psi1_repeat_period_vals[] = { + { 0, "1 multiframe"}, + { 1, "2 multiframes"}, + { 2, "3 multiframes"}, + { 3, "4 multiframes"}, + { 4, "5 multiframes"}, + { 5, "6 multiframes"}, + { 6, "7 multiframes"}, + { 7, "8 multiframes"}, + { 8, "9 multiframes"}, + { 9, "10 multiframes"}, + { 10, "11 multiframes"}, + { 11, "12 multiframes"}, + { 12, "13 multiframes"}, + { 13, "14 multiframes"}, + { 14, "15 multiframes"}, + { 15, "16 multiframes"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_pbcch_pb_vals[] = { + { 0, "0 dB"}, + { 1, "-2 dB"}, + { 2, "-4 dB"}, + { 3, "-6 dB"}, + { 4, "-8 dB"}, + { 5, "-10 dB"}, + { 6, "-12 dB"}, + { 7, "-14 dB"}, + { 8, "-16 dB"}, + { 9, "-18 dB"}, + { 10, "-20 dB"}, + { 11, "-22 dB"}, + { 12, "-24 dB"}, + { 13, "-26 dB"}, + { 14, "-28 dB"}, + { 15, "-30 dB"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_spgc_ccch_sup_value = { + "SPLIT_PG_CYCLE is supported on CCCH in this cell", + "SPLIT_PG_CYCLE is not supported on CCCH in this cell" +}; + +static const value_string gsm_a_rr_priority_access_thr_vals[] = { + { 0, "Packet access is not allowed in the cell"}, + { 1, "Packet access is not allowed in the cell"}, + { 2, "Packet access is not allowed in the cell"}, + { 3, "Packet access is allowed for priority level 1"}, + { 4, "Packet access is allowed for priority level 1 to 2"}, + { 5, "Packet access is allowed for priority level 1 to 3"}, + { 6, "Packet access is allowed for priority level 1 to 4"}, + { 7, "Packet access is allowed for priority level 1 to 4"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_gprs_mobile_allocation(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 value; + guint64 ma_length; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_hsn, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RFL number list", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rfl_number, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MA", "Not Present", "Present")) + { + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "ARFCN index list", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_arfcn_index, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + } + else + { + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_ma_length, tvb, curr_bit_offset, 6, &ma_length, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + value = (gint)ma_length + 1; + item = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (value>>3)+1, "MA Bitmap: "); + while (value) + { + proto_item_append_text(item,"%d",tvb_get_bits8(tvb,curr_bit_offset,1)); + curr_bit_offset += 1; + value -= 1; + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_nmo_vals[] = { + { 0, "Network Mode of Operation I"}, + { 1, "Network Mode of Operation II"}, + { 2, "Network Mode of Operation III"}, + { 3, "Reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t3168_vals[] = { + { 0, "500 ms"}, + { 1, "1000 ms"}, + { 2, "1500 ms"}, + { 3, "2000 ms"}, + { 4, "2500 ms"}, + { 5, "3000 ms"}, + { 6, "3500 ms"}, + { 7, "4000 ms"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t3192_vals[] = { + { 0, "500 ms"}, + { 1, "1000 ms"}, + { 2, "1500 ms"}, + { 3, "0 ms"}, + { 4, "80 ms"}, + { 5, "120 ms"}, + { 6, "160 ms"}, + { 7, "200 ms"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_drx_timer_max_vals[] = { + { 0, "0 s"}, + { 1, "1 s"}, + { 2, "2 s"}, + { 3, "4 s"}, + { 4, "8 s"}, + { 5, "16 s"}, + { 6, "32 s"}, + { 7, "64 s"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_access_burst_type_value = { + "11-bit format shall be used", + "8-bit format shall be used" +}; + +static const true_false_string gsm_a_rr_control_ack_type_value = { + "Default format is RLC/MAC control block", + "Default format is four access bursts" +}; + +static const value_string gsm_a_rr_pan_max_vals[] = { + { 0, "maximum value allowed for counter N3102 is 4"}, + { 1, "maximum value allowed for counter N3102 is 8"}, + { 2, "maximum value allowed for counter N3102 is 12"}, + { 3, "maximum value allowed for counter N3102 is 16"}, + { 4, "maximum value allowed for counter N3102 is 20"}, + { 5, "maximum value allowed for counter N3102 is 24"}, + { 6, "maximum value allowed for counter N3102 is 28"}, + { 7, "maximum value allowed for counter N3102 is 32"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_egprs_packet_channel_request_value = { + "Use two phase packet access with PACKET CHANNEL REQUEST message for uplink TBF establishment on the PRACH", + "Use EGPRS PACKET CHANNEL REQUEST message for uplink TBF establishment on the PRACH" +}; + +static const value_string gsm_a_rr_bep_period_vals[] = { + { 0, "1"}, + { 1, "2"}, + { 2, "3"}, + { 3, "4"}, + { 4, "5"}, + { 5, "7"}, + { 6, "10"}, + { 7, "12"}, + { 8, "15"}, + { 9, "20"}, + { 10, "25"}, + { 11, "Reserved"}, + { 12, "Reserved"}, + { 13, "Reserved"}, + { 14, "Reserved"}, + { 15, "Reserved"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pfc_feature_mode_value = { + "The network supports packet flow context procedures", + "The network does not support packet flow context procedures" +}; + +static const true_false_string gsm_a_rr_dtm_support_value = { + "The cell supports DTM procedures", + "The cell does not support DTM procedures" +}; + +static const true_false_string gsm_a_rr_bss_paging_coordination_value = { + "The cell supports Circuit-Switched paging coordination", + "The cell does not support Circuit-Switched paging coordination" +}; + +static const true_false_string gsm_a_rr_ccn_active_value = { + "CCN is enabled in the cell", + "CCN is disabled in the cell" +}; + +static const true_false_string gsm_a_rr_nw_ext_utbf_value = { + "The extended uplink TBF mode is supported by the network", + "The extended uplink TBF mode is not supported by the network" +}; + +static const true_false_string gsm_a_rr_multiple_tbf_capability_value = { + "The cell supports multiple TBF procedures", + "The cell does not support multiple TBF procedures" +}; + +static const true_false_string gsm_a_rr_ext_utbf_no_data_value = { + "The mobile station may refrain from sending a PACKET UPLINK DUMMY CONTROL BLOCK message when there is no other RLC/MAC block ready to send in an uplink radio block allocated by the network", + "The mobile station shall send a PACKET UPLINK DUMMY CONTROL BLOCK message when there is no other RLC/MAC block ready to send in an uplink radio block allocated by the network" +}; + +static const true_false_string gsm_a_rr_dtm_enhancements_capability_value = { + "The cell supports enhanced DTM CS establishment and enhanced DTM CS release procedures", + "The cell does not support enhanced DTM CS establishment and enhanced DTM CS release procedures" +}; + +static const true_false_string gsm_a_rr_reduced_latency_access_value = { + "The cell supports \"One Phase Access Request by Reduced Latency MS\"", + "The cell does not support \"One Phase Access Request by Reduced Latency MS\"" +}; + +static const value_string gsm_a_rr_alpha_vals[] = { + { 0, "0.0"}, + { 1, "0.1"}, + { 2, "0.2"}, + { 3, "0.3"}, + { 4, "0.4"}, + { 5, "0.5"}, + { 6, "0.6"}, + { 7, "0.7"}, + { 8, "0.8"}, + { 9, "0.9"}, + { 10, "1.0"}, + { 11, "1.0"}, + { 12, "1.0"}, + { 13, "1.0"}, + { 14, "1.0"}, + { 15, "1.0"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t_avg_x_vals[] = { + { 0, "2^(0/2) / 6 multiframes"}, + { 1, "2^(1/2) / 6 multiframes"}, + { 2, "2^(2/2) / 6 multiframes"}, + { 3, "2^(3/2) / 6 multiframes"}, + { 4, "2^(4/2) / 6 multiframes"}, + { 5, "2^(5/2) / 6 multiframes"}, + { 6, "2^(6/2) / 6 multiframes"}, + { 7, "2^(7/2) / 6 multiframes"}, + { 8, "2^(8/2) / 6 multiframes"}, + { 9, "2^(9/2) / 6 multiframes"}, + { 10, "2^(10/2) / 6 multiframes"}, + { 11, "2^(11/2) / 6 multiframes"}, + { 12, "2^(12/2) / 6 multiframes"}, + { 13, "2^(13/2) / 6 multiframes"}, + { 14, "2^(14/2) / 6 multiframes"}, + { 15, "2^(15/2) / 6 multiframes"}, + { 16, "2^(16/2) / 6 multiframes"}, + { 17, "2^(17/2) / 6 multiframes"}, + { 18, "2^(18/2) / 6 multiframes"}, + { 19, "2^(19/2) / 6 multiframes"}, + { 20, "2^(20/2) / 6 multiframes"}, + { 21, "2^(21/2) / 6 multiframes"}, + { 22, "2^(22/2) / 6 multiframes"}, + { 23, "2^(23/2) / 6 multiframes"}, + { 24, "2^(24/2) / 6 multiframes"}, + { 25, "2^(25/2) / 6 multiframes"}, + { 26, "2^(25/2) / 6 multiframes"}, + { 27, "2^(25/2) / 6 multiframes"}, + { 28, "2^(25/2) / 6 multiframes"}, + { 29, "2^(25/2) / 6 multiframes"}, + { 30, "2^(25/2) / 6 multiframes"}, + { 31, "2^(25/2) / 6 multiframes"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pc_meas_chan_value = { + "Downlink measurements for power control shall be made on PDCH", + "Downlink measurements for power control shall be made on BCCH" +}; + +static const value_string gsm_a_rr_n_avg_i_vals[] = { + { 0, "2^(0/2)"}, + { 1, "2^(1/2)"}, + { 2, "2^(2/2)"}, + { 3, "2^(3/2)"}, + { 4, "2^(4/2)"}, + { 5, "2^(5/2)"}, + { 6, "2^(6/2)"}, + { 7, "2^(7/2)"}, + { 8, "2^(8/2)"}, + { 9, "2^(9/2)"}, + { 10, "2^(10/2)"}, + { 11, "2^(11/2)"}, + { 12, "2^(12/2)"}, + { 13, "2^(13/2)"}, + { 14, "2^(14/2)"}, + { 15, "2^(15/2)"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_sgsnr_value = { + "SGSN is Release '99 onwards", + "SGSN is Release '98 or older" +}; + +static const true_false_string gsm_a_rr_si_status_ind_value = { + "The network supports the PACKET SI STATUS message", + "The network does not support the PACKET SI STATUS message" +}; + +static const value_string gsm_a_rr_lb_ms_txpwr_max_cch_vals[] = { + { 0, "43 dBm"}, + { 1, "41 dBm"}, + { 2, "39 dBm"}, + { 3, "37 dBm"}, + { 4, "35 dBm"}, + { 5, "33 dBm"}, + { 6, "31 dBm"}, + { 7, "29 dBm"}, + { 8, "27 dBm"}, + { 9, "25 dBm"}, + { 10, "23 dBm"}, + { 11, "21 dBm"}, + { 12, "19 dBm"}, + { 13, "17 dBm"}, + { 14, "15 dBm"}, + { 15, "13 dBm"}, + { 16, "11 dBm"}, + { 17, "9 dBm"}, + { 18, "7 dBm"}, + { 19, "5 dBm"}, + { 20, "5 dBm"}, + { 21, "5 dBm"}, + { 22, "5 dBm"}, + { 23, "5 dBm"}, + { 24, "5 dBm"}, + { 25, "5 dBm"}, + { 26, "5 dBm"}, + { 27, "5 dBm"}, + { 28, "5 dBm"}, + { 29, "5 dBm"}, + { 30, "5 dBm"}, + { 31, "5 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_si2n_support_vals[] = { + { 0, "SI2n is not supported"}, + { 1, "SI2n is supported on PACCH"}, + { 2, "SI2n is supported on PACCH and broadcast on BCCH"}, + { 3, "SI2n is supported on PACCH and broadcast on BCCH Ext"}, + { 0, NULL } +}; + +static guint16 +de_rr_si13_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + + len = 20; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI13_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI13_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SI13 contents", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_change_mark, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si_change_field, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI13 Change Mark", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13_change_mark, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + bit_offset += de_rr_rest_oct_gprs_mobile_allocation(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "PBCCH", "Present In Cell", "Not Present In Cell")) + { /* PBCCH present in the cell */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_psi1_repeat_period, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PBCCH_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PBCCH_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_pb, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_tsc, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_tn, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "MAIO", "Present", "Not Present")) + { + proto_tree_add_text(subtree2, tvb, bit_offset>>3, 1, "MAIO: %d", tvb_get_bits8(tvb,bit_offset,6)); + bit_offset += 6; + } + else + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "ARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_arfcn, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + else + proto_tree_add_text(subtree2, tvb, bit_offset>>3, 1, "PBCCH shall use the BCCH carrier"); + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + else + { /* PBCCH not present in the cell */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rac, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_spgc_ccch_sup, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_priority_access_thr, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_network_control_order, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + bit_offset += de_rr_rest_oct_gprs_cell_options(tvb, subtree, bit_offset); + bit_offset += de_rr_rest_oct_gprs_power_control_parameters(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in R99", "Present", "Not present")) + { /* Additions in release 99 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_sgsnr, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-4", "Present", "Not present")) + { /* Additions in release Rel-4 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si_status_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-6", "Present", "Not present")) + { /* Additions in release Rel-6 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "LB MS TXPWR MAX CCH ", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_lb_ms_txpwr_max_cch, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2n_support, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets + */ +/* + * [3] 10.5.2.38 Starting Time + */ +static guint16 +de_rr_starting_time(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_item *item; + guint32 curr_offset; + guint16 rfn, fn; + + curr_offset = offset; + + fn = tvb_get_ntohs(tvb,curr_offset); + rfn = reduced_frame_number(fn); + proto_tree_add_item(tree, hf_gsm_a_rr_T1prim, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_T3, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + curr_offset++; + proto_tree_add_item(tree, hf_gsm_a_rr_T2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + item = proto_tree_add_uint(tree, hf_gsm_a_rr_rfn, tvb, curr_offset-2, 2, rfn); + PROTO_ITEM_SET_GENERATED(item); + return(curr_offset - offset); +} +/* + * [3] 10.5.2.39 Synchronization Indication + */ +/* + * ROT: Report Observed Time Difference (Octet1 bit 3) */ + +static const true_false_string sm_a_rr_sync_ind_rot_value = { + "Mobile Time Difference IE shall be included in the HANDOVER COMPLETE message", + "Mobile Time Difference IE shall not be included in the HANDOVER COMPLETE message" +}; + +/* SI: Synchronization indication (octet 1)Bit2 1 */ + +static const value_string gsm_a_rr_sync_ind_si_vals[] = { + { 0, "Non-synchronized"}, + { 1, "Synchronized"}, + { 2, "Pre-synchronised"}, + { 3, "Pseudo-synchronised"}, + { 0, NULL } +}; +/* NCI: Normal cell indication (octet 1, bit 4) */ + +static const true_false_string gsm_a_rr_sync_ind_nci_value = { + "Out of range timing advance shall trigger a handover failure procedure", + "Out of range timing advance is ignored" +}; +static guint16 +de_rr_sync_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + /*NCI */ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_nci, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*ROT */ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_rot, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*SI*/ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_si, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.40 Timing Advance + */ +static guint16 +de_rr_timing_adv(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_timing_adv, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.41 Time Difference + */ +static guint16 +de_rr_time_diff(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_time_diff, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.41a TLLI + * The TLLI is encoded as a binary number with a length of 4 octets. TLLI is defined in 3GPP TS 23.003 + */ +guint16 +de_rr_tlli(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset, tlli; + + curr_offset = offset; + + tlli = tvb_get_ntohl(tvb, curr_offset); + proto_tree_add_item(tree, hf_gsm_a_rr_tlli, tvb, curr_offset, 4, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 4; + if(add_string) + g_snprintf(add_string, string_len, " - 0x%x", tlli); + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42 TMSI/P-TMSI + */ +static guint16 +de_rr_tmsi_ptmsi(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_TMSI_PTMSI].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_TMSI_PTMSI]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_tmsi_ptmsi, tvb, curr_offset, 4, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 4; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42a VGCS target mode Indication + */ +/* +Target mode (octet 3) +Bit 8 7 + 0 0 dedicated mode + 0 1 group transmit mode + Other values are reserved for future use. +*/ +static const value_string gsm_a_rr_target_mode_vals[] _U_ = { + { 0, "Dedicated mode"}, + { 1, "Group transmit mode"}, + { 0, NULL } +}; +static guint16 +de_rr_vgcs_tar_mode_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_target_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_group_cipher_key_number, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42b VGCS Ciphering Parameters + */ +static guint16 +de_rr_vgcs_cip_par(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_text(tree,tvb, curr_offset, len ,"Data(Not decoded)"); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.43 Wait Indication + */ +static guint16 +de_rr_wait_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_wait_indication, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.44 SI10 rest octets $(ASCI)$ + */ + +/* + * [3] 10.5.2.45 Extended Measurement Results + */ +static guint16 +de_rr_ext_meas_result(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset, i; + guint8 value; + + curr_offset = offset; + len = 16; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_seq_code, tvb,bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_dtx_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + for (i=0; i<21; i++) + { + value = tvb_get_bits8(tvb,bit_offset,6); + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "RXLEV carrier %d: %s (%d)",i,val_to_str_ext(value, &gsm_a_rr_rxlev_vals_ext, "Unknown"),value); + bit_offset += 6; + } + + curr_offset = offset + len; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.46 Extended Measurement Frequency List + */ +static guint16 +de_rr_ext_meas_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_seq_code, tvb,(curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + +/* + * [3] 10.5.2.47 Suspension Cause + */ +/*Suspension cause value (octet 2)*/ +static const value_string gsm_a_rr_suspension_cause_vals[] = { + { 0, "Emergency call, mobile originating call or call re-establishment"}, + { 1, "Location Area Update"}, + { 2, "MO Short message service"}, + { 3, "Other procedure which can be completed with an SDCCH"}, + { 4, "MO Voice broadcast or group call"}, + { 5, "Mobile terminating CS connection"}, + { 6, "DTM not supported in the cell"}, + { 0, NULL } +}; +guint16 +de_rr_sus_cau(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_suspension_cause, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.48 APDU ID + */ +static const value_string gsm_a_rr_apdu_id_vals[] = { + { 0, "RRLP (GSM 04.31) LCS" }, + { 0, NULL }, +}; +static guint16 +de_rr_apdu_id(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree_add_item(tree, hf_gsm_a_rr_apdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); + + return 0; +} + +/* + * [3] 10.5.2.49 APDU Flags + */ +static const value_string gsm_a_rr_apdu_flags_vals[] = { + { 1, "Last or only segment" }, + { 2, "First or only segment" }, + { 0, NULL }, +}; +static guint16 +de_rr_apdu_flags(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree_add_item(tree, hf_gsm_a_rr_apdu_flags, tvb, offset, 1, ENC_BIG_ENDIAN); + + return 1; +} + +/* + * [3] 10.5.2.50 APDU Data + */ +static guint16 +de_rr_apdu_data(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + tvbuff_t *sub_tvb; + + sub_tvb = tvb_new_subset(tvb, offset, len, len); + + if (rrlp_dissector) + call_dissector(rrlp_dissector, sub_tvb,pinfo, tree); + + return len; +} + +/* + * [3] 10.5.2.51 Handover To UTRAN Command + */ +static guint16 +de_rr_ho_to_utran_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + tvbuff_t *rrc_irat_ho_to_utran_cmd_tvb; + + curr_offset = offset; + if (len) + { + rrc_irat_ho_to_utran_cmd_tvb = tvb_new_subset(tvb, curr_offset, len, len); + if (rrc_irat_ho_to_utran_cmd_handle) + call_dissector(rrc_irat_ho_to_utran_cmd_handle, rrc_irat_ho_to_utran_cmd_tvb, pinfo, tree); + } + + curr_offset += len; + return(curr_offset - offset); +} + + +/* + * [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell + */ +/* + * 10.5.2.57 Service Support + */ +static const true_false_string gsm_a_rr_MBMS_multicast_value = { + "mobile station requires notification of multicast MBMS services", + "mobile station does not require notification of multicast MBMS services" +}; +static const true_false_string gsm_a_rr_MBMS_broadcast_value = { + "mobile station requires notification of broadcast MBMS services", + "mobile station does not require notification of broadcast MBMS services" +}; +static guint16 +de_rr_serv_sup(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + /* bit 1 + * 0 mobile station does not require notification of broadcast MBMS services + * 1 mobile station requires notification of broadcast MBMS services + * bit 2 + * 0 mobile station does not require notification of multicast MBMS services + * 1 mobile station requires notification of multicast MBMS services + */ + /* MBMS Multicast */ + proto_tree_add_item(tree, hf_gsm_a_rr_MBMS_multicast, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + /* MBMS Broadcast */ + proto_tree_add_item(tree, hf_gsm_a_rr_MBMS_broadcast, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.59 Dedicated Service Information + */ +/* +Last Segment (octet 2) +bit 1 + 0 mobile station shall not perform Service Information Sending procedure on new cell. + 1 mobile station shall perform Service Information Sending procedure on new cell. +*/ +static const true_false_string gsm_a_rr_last_segment_value = { + "Mobile station shall perform Service Information Sending procedure on new cell.", + "mobile station shall not perform Service Information Sending procedure on new cell." +}; +static guint16 +de_rr_ded_serv_inf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_last_segment, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 3; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.69 Carrier Indication + */ +static const true_false_string gsm_a_rr_carrier_ind_value = { + "Carrier 2", + "Carrier 1" +}; + +static guint16 +de_rr_carrier_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_CARRIER_IND].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CARRIER_IND]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_carrier_ind, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} + +guint16 (*rr_elem_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string, int string_len) = { + /* Radio Resource Management Information Elements 10.5.2, most are from 10.5.1 */ + + de_rr_ba_range, /* [3] 10.5.2.1a BA Range */ + de_rr_cell_ch_dsc, /* [3] 10.5.2.1b Cell Channel Description */ + de_rr_ba_list_pref, /* [3] 10.5.2.1c BA List Pref */ + de_rr_utran_freq_list, /* [3] 10.5.2.1d UTRAN Frequency List */ + de_rr_cell_select_indic, /* [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH IE */ + de_rr_cell_dsc, /* 10.5.2.2 RR Cell Description */ + de_rr_cell_opt_bcch, /* [3] 10.5.2.3 Cell Options (BCCH) */ + de_rr_cell_opt_sacch, /* [3] 10.5.2.3a Cell Options (SACCH) */ + de_rr_cell_sel_param, /* [3] 10.5.2.4 Cell Selection Parameters */ +/* + * [3] 10.5.2.4a (void) + */ + de_rr_ch_dsc, /* [3] 10.5.2.5 Channel Description */ + de_rr_ch_dsc2, /* [3] 10.5.2.5a RR Channel Description 2 */ + de_rr_ch_dsc3, /* [3] 10.5.2.5c RR Channel Description 3 */ + de_rr_ch_mode, /* [3] 10.5.2.6 Channel Mode */ + de_rr_ch_mode2, /* [3] 10.5.2.7 Channel Mode 2 */ + de_rr_utran_cm, /* [3] 10.5.2.7a UTRAN Classmark */ +/* [3] 10.5.2.7b (void) */ + + de_rr_cm_enq_mask, /* [3] 10.5.2.7c Classmark Enquiry Mask */ +/* [3] 10.5.2.7d GERAN Iu Mode Classmark information element */ + de_rr_chnl_needed, /* [3] 10.5.2.8 Channel Needed + * [3] 10.5.2.8a (void) + * [3] 10.5.2.8b Channel Request Description 2 */ + /* Pos 20 */ + de_rr_cip_mode_set, /* [3] 10.5.2.9 Cipher Mode Setting */ + de_rr_cip_mode_resp, /* [3] 10.5.2.10 Cipher Response */ + de_rr_ctrl_ch_desc, /* [3] 10.5.2.11 Control Channel Description */ +/* [3] 10.5.2.11a DTM Information Details */ + de_rr_dyn_arfcn_map, /* [3] 10.5.2.11b Dynamic ARFCN Mapping */ + de_rr_freq_ch_seq, /* [3] 10.5.2.12 Frequency Channel Sequence */ + de_rr_freq_list, /* [3] 10.5.2.13 Frequency List */ + de_rr_freq_short_list, /* [3] 10.5.2.14 Frequency Short List */ + de_rr_freq_short_list2, /* [3] 10.5.2.14a Frequency Short List 2 */ +/* [3] 10.5.2.14b Group Channel Description */ + de_rr_gprs_resumption, /* [3] 10.5.2.14c GPRS Resumption */ + de_rr_gprs_broadcast_info, /* [3] 10.5.2.14d GPRS broadcast information */ +/* [3] 10.5.2.14e Enhanced DTM CS Release Indication */ + de_rr_ho_ref, /* 10.5.2.15 Handover Reference */ + de_rr_ia_rest_oct, /* [3] 10.5.2.16 IA Rest Octets */ + de_rr_iar_rest_oct, /* [3] 10.5.2.17 IAR Rest Octets */ + de_rr_iax_rest_oct, /* [3] 10.5.2.18 IAX Rest Octets */ + de_rr_l2_pseudo_len, /*[3] 10.5.2.19 L2 Pseudo Length */ + de_rr_meas_res, /* [3] 10.5.2.20 Measurement Results */ +/* + * [3] (void) + */ + de_rr_mob_all, /* [3] 10.5.2.21 Mobile Allocation */ + de_rr_mob_time_diff, /* [3] 10.5.2.21a Mobile Time Difference */ + de_rr_multirate_conf, /* [3] 10.5.2.21aa MultiRate configuration */ + /* Pos 30 */ + de_rr_mult_all, /* [3] 10.5.2.21b Multislot Allocation */ +/* + * [3] 10.5.2.21c (void) + */ + de_rr_neigh_cell_desc, /* [3] 10.5.2.22 Neighbour Cell Description */ + de_rr_neigh_cell_desc2, /* [3] 10.5.2.22a Neighbour Cell Description 2 */ +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets */ + de_rr_p1_rest_oct, /* [3] 10.5.2.23 P1 Rest Octets */ + de_rr_p2_rest_oct, /* [3] 10.5.2.24 P2 Rest Octets */ + de_rr_p3_rest_oct, /* [3] 10.5.2.25 P3 Rest Octets */ + de_rr_packet_ch_desc, /* [3] 10.5.2.25a Packet Channel Description */ + de_rr_ded_mod_or_tbf, /* [3] 10.5.2.25b Dedicated mode or TBF */ +/* [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment + */ + de_rr_page_mode, /* [3] 10.5.2.26 Page Mode */ +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ + de_rr_ncc_perm, /* [3] 10.5.2.27 NCC Permitted */ + de_rr_pow_cmd, /* 10.5.2.28 Power Command */ + de_rr_pow_cmd_and_acc_type, /* 10.5.2.28a Power Command and access type */ + de_rr_rach_ctrl_param, /* [3] 10.5.2.29 RACH Control Parameters */ + de_rr_req_ref, /* [3] 10.5.2.30 Request Reference */ + de_rr_cause, /* 10.5.2.31 RR Cause */ + de_rr_sync_ind, /* 10.5.2.39 Synchronization Indication */ + de_rr_si1_rest_oct, /* [3] 10.5.2.32 SI1 Rest Octets */ +/* [3] 10.5.2.33 SI 2bis Rest Octets */ + de_rr_si2ter_rest_oct, /* [3] 10.5.2.33a SI 2ter Rest Octets */ + de_rr_si2quater_rest_oct, /* [3] 10.5.2.33b SI 2quater Rest Octets */ + de_rr_si3_rest_oct, /* [3] 10.5.2.34 SI3 Rest Octets */ + de_rr_si4_rest_oct, /* [3] 10.5.2.35 SI4 Rest Octets */ + de_rr_si6_rest_oct, /* [3] 10.5.2.35b SI6 Rest Octets */ +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + de_rr_si13_rest_oct, /* [3] 10.5.2.37a SI13 Rest Octets */ +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets */ + de_rr_starting_time, /* [3] 10.5.2.38 Starting Time */ + de_rr_timing_adv, /* [3] 10.5.2.40 Timing Advance */ + de_rr_time_diff, /* [3] 10.5.2.41 Time Difference */ + de_rr_tlli, /* [3] 10.5.2.41a TLLI */ + de_rr_tmsi_ptmsi, /* [3] 10.5.2.42 TMSI/P-TMSI */ + de_rr_vgcs_tar_mode_ind, /* [3] 10.5.2.42a VGCS target mode Indication */ + /* Pos 40 */ + de_rr_vgcs_cip_par, /* [3] 10.5.2.42b VGCS Ciphering Parameters */ + de_rr_wait_ind, /* [3] 10.5.2.43 Wait Indication */ +/* [3] 10.5.2.44 SI10 rest octets $(ASCI)$ */ + de_rr_ext_meas_result, /* [3] 10.5.2.45 Extended Measurement Results */ + de_rr_ext_meas_freq_list, /* [3] 10.5.2.46 Extended Measurement Frequency List */ + de_rr_sus_cau, /* [3] 10.5.2.47 Suspension Cause */ + de_rr_apdu_id, /* [3] 10.5.2.48 APDU ID */ + de_rr_apdu_flags, /* [3] 10.5.2.49 APDU Flags */ + de_rr_apdu_data, /* [3] 10.5.2.50 APDU Data */ + de_rr_ho_to_utran_cmd, /* [3] 10.5.2.51 Handover To UTRAN Command */ +/* [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell + * 10.5.2.57 Service Support */ + de_rr_serv_sup, /* 10.5.2.57 Service Support */ +/* + * 10.5.2.58 MBMS p-t-m Channel Description + */ + de_rr_ded_serv_inf, /* [3] 10.5.2.59 Dedicated Service Information */ +/* + * 10.5.2.60 MPRACH Description + * 10.5.2.61 Restriction Timer + * 10.5.2.62 MBMS Session Identity + * 10.5.2.63 Reduced group or broadcast call reference + * 10.5.2.64 Talker Priority status + * 10.5.2.65 Talker Identity + * 10.5.2.66 Token + * 10.5.2.67 PS Cause + * 10.5.2.68 VGCS AMR Configuration + */ + de_rr_carrier_ind, /* 10.5.2.69 Carrier Indication */ + NULL, /* NONE */ +}; + +/* MESSAGE FUNCTIONS */ + +/* + * 9.1.1 Additional Assignment + */ +static void +dtap_rr_add_ass(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + /* Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x72, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + + /* Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7c, GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * 9.1.2 Assignment command + */ +static void +dtap_rr_ass_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, after time"); + + /* Power Command 10.5.2.28 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_POW_CMD, NULL); + + /* 05 Frequency List 10.5.2.13 C TLV 4-132 */ + ELEM_OPT_TLV(0x05, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, after time"); + + /* 62 Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62, GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* 10 Multislot Allocation 10.5.2.21b C TLV 3-12 */ + ELEM_OPT_TLV(0x10,GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, " - Description of the multislot configuration"); + + /* 63 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of the First Channel(Channel Set 1)"); + + /* 11 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* 13 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* 14 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* 15 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* 16 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* 17 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* 18 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + /* 64 Channel Description 10.5.2.5 O TV 4 */ + ELEM_OPT_TV(0x64,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, "Description of the Second Channel, after time"); + + /* 66 Channel Mode 2 10.5.2.7 O TV 2 */ + ELEM_OPT_TV(0x66,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE2, " - Mode of the Second Channel"); + + /* 72 Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x72,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, after time"); + + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* 19 Frequency List 10.5.2.13 C TLV 4-132 */ + ELEM_OPT_TLV(0x19, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, before time"); + + /* 1C Channel Description 2 10.5.2.5a O TV 4 */ + ELEM_OPT_TV(0x1c,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, before time"); + + /* 1D Channel Description 10.5.2.5 O TV 4 */ + ELEM_OPT_TV(0x1d,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, before time"); + + /* 1E Frequency channel sequence 10.5.2.12 C TV 10 */ + ELEM_OPT_TV(0x1e,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency channel sequence before time"); + + /* 21 Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x21,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, before time"); + + /* 9- Cipher Mode Setting 10.5.2.9 O TV 1 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, NULL); + /* 01 VGCS target mode Indication VGCS target mode Indication 10.5.2.42a O TLV 3 */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* 03 Multi-Rate configuration, MultiRate configuration 10.5.2.21aa O TLV 4-8 */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + /* 04 VGCS Ciphering Parameters VGCS Ciphering Parameters 10.5.2.42b O TLV 3-15 */ + ELEM_OPT_TLV(0x04,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_CIP_PAR, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.3 Assignment complete + */ +static void +dtap_rr_ass_comp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.4 Assignment failure + */ +static void +dtap_rr_ass_fail(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.5 Channel Mode Modify + */ +static void +dtap_rr_ch_mode_mod(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, NULL); + + /* Channel Mode 10.5.2.6 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, NULL); + + /* 01 VGCS target mode Indication VGCS target mode Indication 10.5.2.42a O TLV 3 */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* 03 Multi-Rate configuration, MultiRate configuration 10.5.2.21aa O TLV 4-8 */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.6 Channel Mode Modify Acknowledge + */ +static void +dtap_rr_ch_mode_mod_ack(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, NULL); + + /* Channel Mode 10.5.2.6 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.7 Channel Release + */ +static void +dtap_rr_ch_rel(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + /* 73 BA Range BA Range 10.5.2.1a O TLV 6-7 */ + ELEM_OPT_TLV(0x73, GSM_A_PDU_TYPE_RR, DE_RR_BA_RANGE, NULL); + + /* 74 Group Channel Description Group Channel Description 10.5.2.14b O TLV 5-13 */ + /* ELEM_OPT_TLV(0x74, GSM_A_PDU_TYPE_RR, DE_GRP_CH_DESC, NULL); */ + + /* 8x Group Cipher Key Number Group Cipher Key Number 10.5.1.10 C TV 1 */ + /* ELEM_OPT_TV_SHORT(0x80, GSM_A_PDU_TYPE_RR, DE_GRP_CIP_KEY_NUM, NULL); */ + + /* Cx GPRS Resumption GPRS Resumption 10.5.2.14c O TV 1 */ + ELEM_OPT_TV_SHORT(0xC0, GSM_A_PDU_TYPE_RR, DE_RR_GPRS_RESUMPTION, NULL); + + /* 75 BA List Pref BA List Pref 10.5.2.1c O TLV 3-? */ + ELEM_OPT_TLV(0x75, GSM_A_PDU_TYPE_RR, DE_RR_BA_LIST_PREF, NULL); + + /* 76 UTRAN Freq List 10.5.2.1d O TLV 3-? */ + ELEM_OPT_TLV(0x76, GSM_A_PDU_TYPE_RR, DE_RR_UTRAN_FREQ_LIST, NULL); + + /* 62 Cell Channel Description Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62, GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* 62 Cell selection indicator after release of all TCH and SDCCH 10.5.2.1e O TLV 4-? */ + ELEM_OPT_TLV(0x77, GSM_A_PDU_TYPE_RR, DE_RR_CELL_SELECT_INDIC, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.8 Channel Request + */ +/* This message is NOT follow the basic format, and is only found on RACH - ignored here */ + +/* + * 9.1.9 Ciphering Mode Command + */ +static void +dtap_rr_cip_mode_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Ciphering Mode Setting 10.5.2.9 M V 0.5 */ + /* Cipher Response 10.5.2.10 M V 0.5 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, DE_RR_CIP_MODE_RESP); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} +/* + * 9.1.10 Ciphering Mode Complete + */ +void +dtap_rr_cip_mode_cpte(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mobile Equipment Identity 10.5.1.4 O TLV */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, "Mobile Equipment Identity"); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.11 Classmark change + */ +static void +dtap_rr_mm_cm_change(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mobile Station Classmark 2 10.5.1.6 M LV 4 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MS_CM_2, NULL); + /* 20 Mobile Station Classmark 3 10.5.1.7 C TLV 3-34 */ + ELEM_OPT_TLV(0x20, GSM_A_PDU_TYPE_COMMON, DE_MS_CM_3, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.11 UTRAN Classmark Change + */ +static void +dtap_rr_utran_classmark_change(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* UTRAN Classmark 10.5.2.7a M LV 2-? */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_UTRAN_CM, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * 9.1.12 Classmark enquiry + */ +static void +dtap_rr_cm_enq(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* 10 Classmark Enquiry Mask 10.5.2.7c O TLV 3 */ + ELEM_OPT_TLV(0x10, GSM_A_PDU_TYPE_RR, DE_RR_CM_ENQ_MASK, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.12b Configuration change command + */ +static void +dtap_rr_conf_change_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Multislot Allocation 10.5.2.21b M LV 2-11 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, NULL); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 1"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); +} + +/* + * 9.1.12c Configuration change acknowledge + */ +/* empty message */ + +/* + * 9.1.12d Configuration change reject + */ +static void +dtap_rr_conf_change_rej(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + + +/* + * 9.1.13 Frequency Redefinition + */ +static void +dtap_rr_freq_redef(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + + /* Starting Time 10.5.2.38 M V 2 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62,GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* Carrier Indication 10.5.2.69 O TV 1 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CARRIER_IND,NULL); + + /* Mobile Allocation 10.5.2.21 O TLV 1-9 */ + ELEM_OPT_TLV(0x11, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation C2"); + + /* Channel Description 3 10.5.2.5c O TV 3 */ + ELEM_OPT_TV(0x12,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC3, " - Channel Description C2"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); +} + +/* + * 9.1.13b GPRS suspension request + */ +static void +dtap_rr_gprs_sus_req(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* TLLI 10.5.2.41a M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TLLI, NULL); + + /* Routeing Area Identification 10.5.5.15 M V 6 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_GM, DE_RAI, NULL); + /* Suspension cause 10.5.2.47 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SUS_CAU, NULL); + + /* 01 Service Support 10.5.2.57 O TV 2 */ + ELEM_OPT_TV_SHORT(0x01,GSM_A_PDU_TYPE_RR, DE_RR_SERV_SUP,NULL); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.14 Handover Access + */ +/* This message does NOT follow the basic format, and is only found on DCH during initial handover access */ + +/* 3GPP TS 24.008 version 4.7.0 Release 4 + * [3] 9.1.15 + */ +void +dtap_rr_ho_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mandatory Elemets + * Cell description 10.5.2.2 + */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_DSC, NULL); + + /* Description of the first channel,after time + * Channel Description 2 10.5.2.5a + */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the first channel, after time"); + + /* Handover Reference 10.5.2.15 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_HO_REF, NULL); + + /* Power Command and Access type 10.5.2.28a */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_POW_CMD_AND_ACC_TYPE, NULL); + + /* optional elements */ + + /* Synchronization Indication 10.5.2.39 */ + ELEM_OPT_TV_SHORT(0xD0,GSM_A_PDU_TYPE_RR, DE_RR_SYNC_IND,NULL); + + /* Frequency Short List 10.5.2.14 */ + ELEM_OPT_TV(0x02,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_SHORT_LIST," - Frequency Short List, after time"); + + /* Frequency List 10.5.2.13 */ + ELEM_OPT_TLV(0x05, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, after time"); + + /* Cell Channel Description 10.5.2.1b */ + ELEM_OPT_TV(0x62,GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* Multislot Allocation 10.5.2.21b */ + ELEM_OPT_TLV(0x10,GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, NULL); + + /* Mode of the First Channel(Channel Set 1)) Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of the First Channel(Channel Set 1))"); + + /* Mode of Channel Set 2 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* Mode of Channel Set 3 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* Mode of Channel Set 4 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* Mode of Channel Set 5 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* Mode of Channel Set 6 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* Mode of Channel Set 7 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* Mode of Channel Set 8 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + /* Description of the Second Channel, after time, Channel Description 10.5.2.5 */ + ELEM_OPT_TV(0x64,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, after time"); + + /* Mode of the Second Channel, Channel Mode 2 10.5.2.7 */ + ELEM_OPT_TV(0x66,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE2, " - Mode of the Second Channel"); + + /* Frequency Channel Sequence, after time, Frequency Channel Sequence 10.5.2.12 */ + ELEM_OPT_TV(0x69,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency Channel Sequence, after time"); + + /* Mobile Allocation, after time, Mobile Allocation 10.5.2.21 */ + ELEM_OPT_TLV(0x72,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, after time"); + + /* Starting Time 10.5.2.38 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* Real Time Difference, Time Difference 10.5.2.41 */ + ELEM_OPT_TV(0x7B,GSM_A_PDU_TYPE_RR, DE_RR_TIME_DIFF, " - Real Time Difference"); + + /* Timing Advance, Timing Advance 10.5.2.40 */ + ELEM_OPT_TV(0x7D,GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + + /* Frequency Short List, before time, Frequency Short List 10.5.2.14 */ + ELEM_OPT_TV(0x12,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_SHORT_LIST, " - Frequency Short List, before time"); + + /* Frequency List, before time, Frequency List 10.5.2.13 */ + ELEM_OPT_TLV(0x19,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, before time"); + + /* Description of the First Channel, before time, Channel Description 2 10.5.2.5a*/ + ELEM_OPT_TV(0x1c,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, before time"); + + /* Description of the Second Channel, before time, Channel Description 10.5.2.5*/ + ELEM_OPT_TV(0x1d,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, before time"); + + /* Frequency channel sequence before time, Frequency channel sequence 10.5.2.12*/ + ELEM_OPT_TV(0x1e,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency channel sequence before time"); + + /* Mobile Allocation, before time, Mobile Allocation 10.5.2.21 */ + ELEM_OPT_TLV(0x21,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, before time"); + + /* Cipher Mode Setting, Cipher Mode Setting 10.5.2.9 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, NULL); + + /* VGCS target mode Indication, VGCS target mode Indication 10.5.2.42a */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* Multi-Rate configuration, MultiRate configuration 10.5.2.21a */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + /* Dynamic ARFCN Mapping, Dynamic ARFCN Mapping 10.5.2.11b */ + ELEM_OPT_TLV(0x76,GSM_A_PDU_TYPE_RR, DE_RR_DYN_ARFCN_MAP, NULL); + + /* VGCS Ciphering Parameters, VGCS Ciphering Parameters 10.5.2.42b */ + ELEM_OPT_TLV(0x04,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_CIP_PAR, NULL); + + /* Dedicated Service Information, Dedicated Service Information 10.5.2.59 */ + ELEM_OPT_TV(0x51,GSM_A_PDU_TYPE_RR, DE_RR_DED_SERV_INF, NULL); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.15a Inter System To UTRAN Handover Command + */ +static void +dtap_rr_inter_syst_to_utran_ho_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* UTRAN Classmark 10.5.2.51 M LV 2-? */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_HO_TO_UTRAN_CMD, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* 3GPP TS 24.008 version 4.7.0 Release 4 + * [3] 9.1.16 + */ +static void +dtap_rr_ho_cpte(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + /* 77 Mobile Observed Time Difference Mobile Time Difference 10.5.2.21a */ + ELEM_OPT_TLV(0x77,GSM_A_PDU_TYPE_RR, DE_RR_MOB_TIME_DIFF, " - Mobile Observed Time Difference"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.17 Handover failure + */ +static void +dtap_rr_ho_fail(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.18 Immediate assignment See 3GPP TS 44.018 + */ +static void +dtap_rr_imm_ass(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + guint8 oct; + + curr_offset = offset; + curr_len = len; + + oct = tvb_get_guint8(tvb, curr_offset); + + /* NOTE: The order of the mandatory information elements should be chosen so that + * information elements with 1/2 octet of content (type 1) go together in succession. + * The first type 1 information element occupies bits 1 to 4 of octet N, + * the second bits 5 to 8 of octet N, the third bits 1 to 4 of octet N+1 etc. + * If the number of type 1 information elements is odd then bits 5 to 8 of the last octet + * occupied by these information elements should be treated as spare bits, + * i.e. coded with a "0" in each. + */ + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Dedicated mode or TBF 10.5.2.25b M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_DED_MOD_OR_TBF); + + if((oct&0x10) == 0){ + /* Channel Description 10.5.2.5 C V 3m */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + }else{ + /* Packet Channel Description 10.5.2.25a C V 3 + * If the Dedicated mode or TBF IE indicates that the message assigns a Temporary Block Flow (TBF), + * the mobile station shall consider this information element present in the message. + * If the Dedicated mode or TBF IE indicates that this message is the first of two in a two-message + * assignment of an uplink or downlink TBF, the mobile station shall ignore the contents + * of this information element and regard it as an unnecessary IE. + */ + if((oct&0x04) == 0){ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_PACKET_CH_DESC, NULL); + } + } + /* Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, NULL); + + /* Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + /* IA Rest Octets 10.5.2.16 M V 0-11 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IA_REST_OCT, NULL); + +} + +/* + * 9.1.19 Immediate assignment extended + */ +static void +dtap_rr_imm_ass_ext(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Spare Half Octet 10.5.1.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_SPARE_NIBBLE); + /* Channel Description 1 Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Channel Description 1"); + /* Request Reference 1 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 1"); + /* Timing Advance 1 Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, " - Timing Advance 1"); + /* Channel Description 2 Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Channel Description 2"); + /* Request Reference 2 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 2"); + /* Timing Advance 2 Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, " - Timing Advance 2"); + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + /* IAX Rest Octets 10.5.2.18 M V 0-4 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IAX_REST_OCT, NULL); + +} + +/* + * 9.1.20 Immediate assignment reject + */ +static void +dtap_rr_imm_ass_rej(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Spare Half Octet 10.5.1.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_SPARE_NIBBLE); + /* Request Reference 1 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 1"); + /* Wait Indication 1 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 1"); + /* Request Reference 2 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 2"); + /* Wait Indication 2 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 2"); + /* Request Reference 3 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 3"); + /* Wait Indication 3 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 3"); + /* Request Reference 4 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 4"); + /* Wait Indication 4 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 4"); + /* IAR Rest Octets 10.5.2.19 M V 3 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IAR_REST_OCT, NULL); + +} + +/* + * 9.1.21 Measurement report + */ +static void +dtap_rr_meas_rep(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Measurement Results 10.5.2.20 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_MEAS_RES, NULL); +} + +/* + * 9.1.22 Paging Request Type 1 + */ +static void +dtap_rr_paging_req_type_1(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR Mobile Identity 10.5.1.4 M LV 2-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 1"); + + /* RR Mobile Identity 10.5.1.4 O TLV 3-10 */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 2"); + + /* RR P1 Rest Octets 10.5.2.23 M V 0-17 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P1_REST_OCT, NULL); + +} + +/* + * 9.1.23 Paging Request Type 2 + */ +static void +dtap_rr_paging_req_type_2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 1"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 2"); + + /* RR Mobile Identity 10.5.1.4 O TLV 3-10 */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 3"); + + /* RR P2 Rest Octets 10.5.2.24 M V 1-11 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P2_REST_OCT, NULL); + +} + +/* + * 9.1.24 Paging Request Type 3 + */ +static void +dtap_rr_paging_req_type_3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 1"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 2"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 3"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 4"); + + /* RR P3 Rest Octets 10.5.2.25 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P3_REST_OCT, NULL); + +} + +/* + * [4] 9.1.25 Paging response + */ +static void +dtap_rr_paging_resp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + guint8 oct; + proto_tree *subtree; + proto_item *item; + + curr_offset = offset; + curr_len = len; + + /* + * special dissection for Cipher Key Sequence Number + */ + oct = tvb_get_guint8(tvb, curr_offset); + + other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8); + proto_tree_add_text(tree, + tvb, curr_offset, 1, + "%s : Spare", + a_bigbuf); + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_common_elem_strings[DE_CIPH_KEY_SEQ_NUM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_CIPH_KEY_SEQ_NUM]); + + other_decode_bitfield_value(a_bigbuf, oct, 0x08, 8); + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Spare", + a_bigbuf); + + other_decode_bitfield_value(a_bigbuf, oct, 0x07, 8); + + switch (oct & 0x07) + { + case 0x07: + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Ciphering Key Sequence Number: No key is available", + a_bigbuf); + break; + + default: + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Ciphering Key Sequence Number: %u", + a_bigbuf, + oct & 0x07); + break; + } + + curr_offset++; + curr_len--; + + if ((signed)curr_len <= 0) return; + + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MS_CM_2, NULL); + + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MID, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.26 Partial Release + */ +static void +dtap_rr_partial_rel(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.27 Partial Release Complete + */ +/* empty message */ + +/* + * [4] 9.1.28 Physical Information + */ +static void +dtap_rr_phy_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.29 + */ +static void +dtap_rr_rr_status(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.31 + */ +static void +dtap_rr_sys_info_1(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI1_REST_OCT, NULL); +} + +/* + * [4] 9.1.32 + */ +static void +dtap_rr_sys_info_2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NCC_PERM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); +} + +/* + * [4] 9.1.33 + */ +static void +dtap_rr_sys_info_2bis(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - Extended BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); +} + +/* + * [4] 9.1.34 + */ +static void +dtap_rr_sys_info_2ter(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC2, " - Extended BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI2TER_REST_OCT, NULL); +} + +/* + * [4] 9.1.34a + */ +static void +dtap_rr_sys_info_2quater(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI2QUATER_REST_OCT, NULL); +} + +/* + * [4] 9.1.35 + */ +static void +dtap_rr_sys_info_3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_CELL_ID, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CTRL_CH_DESC, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_OPT_BCCH, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_SEL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI3_REST_OCT, NULL); +} + +/* + * [4] 9.1.36 + */ +static void +dtap_rr_sys_info_4(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_SEL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_OPT_TV(0x64, GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - CBCH"); + + ELEM_OPT_TLV(0x72, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - CBCH"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI4_REST_OCT, NULL); +} + +/* + * [4] 9.1.37 + */ +static void +dtap_rr_sys_info_5(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - BCCH Frequency List"); +} + +/* + * [4] 9.1.38 + */ +static void +dtap_rr_sys_info_5bis(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - Extended BCCH Frequency List"); +} + +/* + * [4] 9.1.39 + */ +static void +dtap_rr_sys_info_5ter(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC2, " - Extended BCCH Frequency List"); +} + +/* + * [4] 9.1.40 + */ +static void +dtap_rr_sys_info_6(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_CELL_ID, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_OPT_SACCH, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NCC_PERM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI6_REST_OCT, NULL); +} + +/* + * [4] 9.1.43a + */ +static void +dtap_rr_sys_info_13(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI13_REST_OCT, NULL); +} + +/* + * [4] 9.1.51 Extended Measurement Order + */ +static void +dtap_rr_ext_meas_order(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Extended Measurement Frequency List 10.5.2.46 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_EXT_MEAS_FREQ_LIST, NULL); +} + +/* + * [4] 9.1.52 Extended Measurement Report + */ +static void +dtap_rr_ext_meas_report(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Extended Measurement Result 10.5.2.45 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_EXT_MEAS_RESULT, NULL); +} + +/* + * 9.1.53 Application Information + */ +static void +dtap_rr_app_inf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_ID, NULL); + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_FLAGS, NULL); + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_APDU_DATA, NULL); +} + +/* + * [4] 9.1.54 Measurement Information + */ +static const value_string gsm_a_rr_3g_wait_vals[] = { + { 0, "1 instance that contain 3G Neighbour Cell Description shall be received"}, + { 1, "2 instances that contain 3G Neighbour Cell Description shall be received"}, + { 2, "3 instances that contain 3G Neighbour Cell Description shall be received"}, + { 3, "4 instances that contain 3G Neighbour Cell Description shall be received"}, + { 4, "5 instances that contain 3G Neighbour Cell Description shall be received"}, + { 5, "6 instances that contain 3G Neighbour Cell Description shall be received"}, + { 6, "7 instances that contain 3G Neighbour Cell Description shall be received"}, + { 7, "8 instances that contain 3G Neighbour Cell Description shall be received"}, + { 0, NULL } +}; + + +static void +sacch_rr_meas_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_) +{ + proto_tree *subtree = NULL, *subtree2 = NULL; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, bit_offset_sav2; + guint8 value, idx; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_3g_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mi_index, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mi_count, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_pwrc, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_gsm_report_type, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_reporting_rate, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset +=1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_invalid_bsic_reporting, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset +=1; + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Real Time Difference Description", "Present", "Not Present")) + { /* Real Time Difference Description */ + bit_offset += de_rr_rtd_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_RTD_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "BSIC Description", "Present", "Not Present")) + { /* BSIC Description */ + bit_offset += de_rr_bsic_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_BSIC_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Report Priority Description", "Present", "Not Present")) + { /* Report Priority Description */ + bit_offset += de_rr_report_priority_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_REPORT_PRIO_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Measurement Parameters Description", "Present", "Not Present")) + { /* Measurement Parameters Description */ + bit_offset += de_rr_meas_param_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_MEAS_PARAM_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "future extensions of the 2G parameters", "Present", "Not Present")) + { /* used for future extensions of the 2G parameters */ + value = tvb_get_bits8(tvb,bit_offset,8); + bit_offset += 8 + value + 1; + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "3G Neighbour Cell Description", "Present", "Not Present")) + { /* 3G Neighbour Cell Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Wait", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_wait, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Index Start 3G", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_index_start_3g, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Absolute Index Start EMR", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_absolute_index_start_emr, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_fdd_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_tdd_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Description", "Present", "Not Present")) + { /* CDMA2000 Description */ + bit_offset_sav2 = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_CDMA2000_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_CDMA2000_DESC]); + bit_offset += 16; /* cdma2000 frequency band + cdma2000 frequency */ + idx = tvb_get_bits8(tvb,bit_offset,5); /* number_cdma2000_cells */ + bit_offset += 5; + while (idx) + { + bit_offset += 9; /* Pilot PN offset */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "additional information for 3X Auxiliary Pilot", "Present", "Not Present")) + { + value = tvb_get_bits8(tvb,bit_offset,3); + switch (value) + { + case 0: + bit_offset += 5; /* TD_MODE + TD_POWER_LEVEL */ + break; + case 1: + bit_offset += 2; /* QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_A + bit(val(WALSH_LEN_A)+6) */ + break; + case 2: + bit_offset += 2; /* QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_B + bit(val(WALSH_LEN_B)+6) */ + bit_offset += 4; /* AUX_TD_POWER_LEVEL + TD_MODE */ + break; + case 3: + bit_offset += 8; /* SR3_PRIM_PILOT + SR3_PILOT_POWER1 + SR3_PILOT_POWER2 */ + break; + case 6: + bit_offset += 10; /* SR3_PRIM_PILOT + SR3_PILOT_POWER1 + SR3_PILOT_POWER2 + QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_C + bit(val(WALSH_LEN_C)+6) */ + if (tvb_get_bits8(tvb,bit_offset,1)) + { + bit_offset += 3; /* 1 + QOF1 */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LENGTH1 + bit(val(WALSH_LENGTH1)+6) */ + } + else + bit_offset += 1; + if (tvb_get_bits8(tvb,bit_offset,1)) + { + bit_offset += 3; /* 1 + QOF2 */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LENGTH2 + bit(val(WALSH_LENGTH2)+6) */ + } + else + bit_offset += 1; + break; + default: + /* decoding sequence is unknown ! */ + return; + } + } + idx -= 1; + } + proto_tree_add_text(subtree2,tvb, bit_offset_sav2>>3, ((bit_offset-bit_offset_sav2)>>3)+1,"Data(Not decoded)"); + proto_item_set_len(item2,((bit_offset-bit_offset_sav2)>>3)+1); + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + guint8 reporting_quant = 0; + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_qsearch_c, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_search_prio, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + reporting_quant = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Reporting Quantity", "Ec/No", "RSCP"); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Multirat Reporting", "Present", "Not Present")) + bit_offset += 2; /* CDMA2000 Multirat Reporting */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Reporting Threshold", "Present", "Not Present")) + bit_offset += 6; /* CDMA2000 Reporting Offset + CDMA2000 Reporting Threshold */ + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-5", "Present", "Not present")) + { /* Additions in Rel-5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description 2", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description 2 */ + bit_offset += de_rr_3g_add_meas_param_desc2(tvb, tree, bit_offset); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-7", "Present", "Not present")) + { /* Additions in Rel-7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "700 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_700_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_700_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "810 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_810_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_810_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + /* Additions in Rel-8 */ + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-8", "Present", "Not present")) + { /* Additions in Rel-8 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Supplementary Parameters Description", "Present", "Not Present")) + { + bit_offset += de_rr_3g_supplementary_param_desc_mi(tvb, tree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN Parameters Description", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_param_desc_mi(tvb, tree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN CSG Description", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_csg_desc_mi(tvb, tree, bit_offset); + } + } + } + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); +} + +static guint32 +sacch_rr_eutran_meas_report(tvbuff_t *tvb, proto_tree *tree, guint32 bit_offset, guint len_in_bit _U_) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + gint8 n_eutran; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT]); + + n_eutran = tvb_get_bits8(tvb,curr_bit_offset,2); + n_eutran += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_n_eutran, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + while ( (n_eutran > 0) && (curr_bit_offset - bit_offset < len_in_bit) ) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_freq_idx, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_cell_id, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_rpt_quantity, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + n_eutran -= 1; + } + + return curr_bit_offset - bit_offset; +} + +/* + * [4] 9.1.55 Enhanced Measurement Information + */ +static const true_false_string gsm_a_rr_bsic_seen_value = { + "One Cell or more with invalid BSIC and allowed NCC part of BSIC is seen", + "No cell with invalid BSIC and allowed NCC part of BSIC is seen" +}; + +static const true_false_string gsm_a_rr_scale_value = { + "+10 dB", + "0 dB" +}; + +static const value_string gsm_a_rr_mean_bep_gmsk_vals[] = { + { 0, "log10(BEP) > -0.60"}, + { 1, "-0.70 < log10(BEP) < -0.60"}, + { 2, "-0.80 < log10(BEP) < -0.70"}, + { 3, "-0.90 < log10(BEP) < -0.80"}, + { 4, "-1.00 < log10(BEP) < -0.90"}, + { 5, "-1.10 < log10(BEP) < -1.00"}, + { 6, "-1.20 < log10(BEP) < -1.10"}, + { 7, "-1.30 < log10(BEP) < -1.20"}, + { 8, "-1.40 < log10(BEP) < -1.30"}, + { 9, "-1.50 < log10(BEP) < -1.40"}, + { 10, "-1.60 < log10(BEP) < -1.50"}, + { 11, "-1.70 < log10(BEP) < -1.60"}, + { 12, "-1.80 < log10(BEP) < -1.70"}, + { 13, "-1.90 < log10(BEP) < -1.80"}, + { 14, "-2.00 < log10(BEP) < -1.90"}, + { 15, "-2.10 < log10(BEP) < -2.00"}, + { 16, "-2.20 < log10(BEP) < -2.10"}, + { 17, "-2.30 < log10(BEP) < -2.20"}, + { 18, "-2.40 < log10(BEP) < -2.30"}, + { 19, "-2.50 < log10(BEP) < -2.40"}, + { 20, "-2.60 < log10(BEP) < -2.50"}, + { 21, "-2.70 < log10(BEP) < -2.60"}, + { 22, "-2.80 < log10(BEP) < -2.70"}, + { 23, "-2.90 < log10(BEP) < -2.80"}, + { 24, "-3.00 < log10(BEP) < -2.90"}, + { 25, "-3.10 < log10(BEP) < -3.00"}, + { 26, "-3.20 < log10(BEP) < -3.10"}, + { 27, "-3.30 < log10(BEP) < -3.20"}, + { 28, "-3.40 < log10(BEP) < -3.30"}, + { 29, "-3.50 < log10(BEP) < -3.40"}, + { 30, "-3.60 < log10(BEP) < -3.50"}, + { 31, "log10(BEP) < -3.60"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cv_bep_vals[] = { + { 0, "1.75 < CV BEP < 2.00"}, + { 1, "1.50 < CV BEP < 1.75"}, + { 2, "1.25 < CV BEP < 1.50"}, + { 3, "1.00 < CV BEP < 1.25"}, + { 4, "0.75 < CV BEP < 1.00"}, + { 5, "0.50 < CV BEP < 0.75"}, + { 6, "0.25 < CV BEP < 0.50"}, + { 7, "0.00 < CV BEP < 0.25"}, + { 0, NULL } +}; + +static void +sacch_rr_enh_meas_report(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + guint8 idx; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_3g_ba_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_bsic_seen, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_scale, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Serving cell data", "Present", "Not Present")) + { /* Serving cell data */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SERVING_CELL_DATA].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SERVING_CELL_DATA]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dtx_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_full_serv_cell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxqual_full_serv_cell, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mean_bep_gmsk, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mean_cv_bep, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nbr_rcvd_blocks, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeated Invalid BSIC Information", "Present", "Not Present")) + { /* Repeated Invalid BSIC Information */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_freq_ncell, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Bitmap Type Reporting", "Present", "Not Present")) + { /* Bitmap Type Reporting */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING]); + idx = 0; + while (((guint)(bit_offset>>3) <= (offset + len)) && (idx < 96)) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Reporting Quantity", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Neighbour Cell List index: %u", idx); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_quantity, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + idx += 1; + } + } + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"Additions for Rel-8", "Present", "Not Present")) + { + gint8 bitmap_length; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_REPORTING_QUANTITY].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_REPORTING_QUANTITY]); + + bitmap_length = tvb_get_bits8(tvb,bit_offset,7); + bitmap_length += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_emr_bitmap_length, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + + /* REPORTING_QUANTITY */ + idx = 0; + while ((guint)(bit_offset>>3) <= (offset + len) && (idx < bitmap_length) ) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Reporting Quantity", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Neighbour Cell List index: %u", idx); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_quantity, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + idx += 1; + } + + /* E-UTRAN Measurement Report */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN Measurement Report", "Present", "Not Present")) + { + bit_offset += sacch_rr_eutran_meas_report(tvb, subtree, bit_offset, len*8-(bit_offset-offset*8)); + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); +} + +#define NUM_GSM_DTAP_MSG_RR (sizeof(gsm_a_dtap_msg_rr_strings)/sizeof(value_string)) +static gint ett_gsm_dtap_msg_rr[NUM_GSM_DTAP_MSG_RR]; +static void (*dtap_msg_rr_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) = { + NULL, /* Reserved */ + dtap_rr_add_ass, /* Additional Assignment */ + dtap_rr_imm_ass, /* 9.1.18 Immediate assignment */ + dtap_rr_imm_ass_ext, /* Immediate Assignment Extended */ + dtap_rr_imm_ass_rej, /* Immediate Assignment Reject */ + + NULL, /* DTM Assignment Failure */ + NULL, /* DTM Reject */ + NULL, /* DTM Request */ + NULL, /* Main DCCH Assignment Command */ + NULL, /* Packet Assignment Command */ + + dtap_rr_cip_mode_cmd, /* Ciphering Mode Command */ + dtap_rr_cip_mode_cpte, /* Ciphering Mode Complete */ + + dtap_rr_conf_change_cmd, /* Configuration Change Command */ + NULL, /* Configuration Change Ack. */ + dtap_rr_conf_change_rej, /* Configuration Change Reject */ + + dtap_rr_ass_cmd, /* 9.1.2 Assignment Command */ + dtap_rr_ass_comp, /* Assignment Complete */ + dtap_rr_ass_fail, /* Assignment Failure */ + dtap_rr_ho_cmd, /* Handover Command */ + dtap_rr_ho_cpte, /* Handover Complete */ + dtap_rr_ho_fail, /* Handover Failure */ + dtap_rr_phy_info, /* Physical Information */ + NULL, /* DTM Assignment Command */ + + NULL, /* RR-cell Change Order */ + NULL, /* PDCH Assignment Command */ + + dtap_rr_ch_rel, /* Channel Release */ + dtap_rr_partial_rel, /* Partial Release */ + NULL, /* Partial Release Complete */ + + dtap_rr_paging_req_type_1, /* Paging Request Type 1 */ + dtap_rr_paging_req_type_2, /* Paging Request Type 2 */ + dtap_rr_paging_req_type_3, /* Paging Request Type 3 */ + dtap_rr_paging_resp, /* Paging Response */ + NULL, /* Notification/NCH */ + NULL, /* Reserved */ + NULL, /* Notification/Response */ + + NULL, /* Reserved */ + + dtap_rr_utran_classmark_change, /* Utran Classmark Change */ + NULL, /* cdma2000 Classmark Change */ + dtap_rr_inter_syst_to_utran_ho_cmd, /* Inter System to UTRAN Handover Command */ + NULL, /* Inter System to cdma2000 Handover Command */ + + NULL, /* System Information Type 8 */ + dtap_rr_sys_info_1, /* System Information Type 1 */ + dtap_rr_sys_info_2, /* System Information Type 2 */ + dtap_rr_sys_info_3, /* System Information Type 3 */ + dtap_rr_sys_info_4, /* System Information Type 4 */ + dtap_rr_sys_info_5, /* System Information Type 5 */ + dtap_rr_sys_info_6, /* System Information Type 6 */ + NULL, /* System Information Type 7 */ + + dtap_rr_sys_info_2bis, /* System Information Type 2bis */ + dtap_rr_sys_info_2ter, /* System Information Type 2ter */ + dtap_rr_sys_info_2quater, /* System Information Type 2quater */ + dtap_rr_sys_info_5bis, /* System Information Type 5bis */ + dtap_rr_sys_info_5ter, /* System Information Type 5ter */ + NULL, /* System Information Type 9 */ + dtap_rr_sys_info_13, /* System Information Type 13 */ + + NULL, /* System Information Type 16 */ + NULL, /* System Information Type 17 */ + + NULL, /* System Information Type 18 */ + NULL, /* System Information Type 19 */ + NULL, /* System Information Type 20 */ + + dtap_rr_ch_mode_mod, /* Channel Mode Modify */ + dtap_rr_rr_status, /* RR Status */ + dtap_rr_ch_mode_mod_ack, /* Channel Mode Modify Acknowledge */ + dtap_rr_freq_redef, /* Frequency Redefinition */ + dtap_rr_meas_rep, /* 9.1.21 Measurement report */ + dtap_rr_mm_cm_change, /* 9.1.11 Classmark Change */ + dtap_rr_cm_enq, /* Classmark Enquiry */ + dtap_rr_ext_meas_report, /* Extended Measurement Report */ + dtap_rr_ext_meas_order, /* Extended Measurement Order */ + dtap_rr_gprs_sus_req, /* 9.1.13b GPRS Suspension Request */ + + NULL, /* VGCS Uplink Grant */ + NULL, /* Uplink Release */ + NULL, /* Reserved */ + NULL, /* Uplink Busy */ + NULL, /* Talker Indication */ + + NULL, /* UTRAN Classmark Change/Handover To UTRAN Command */ /* spec conflict */ + + dtap_rr_app_inf, /* Application Information */ + + NULL, /* NONE */ +}; + +void get_rr_msg_params(guint8 oct, const gchar **msg_str, int *ett_tree, int *hf_idx, msg_fcn *msg_fcn_p) +{ + gint idx; + + *msg_str = match_strval_idx((guint32) (oct & DTAP_RR_IEI_MASK), gsm_a_dtap_msg_rr_strings, &idx); + *hf_idx = hf_gsm_a_dtap_msg_rr_type; + if (*msg_str != NULL) { + *ett_tree = ett_gsm_dtap_msg_rr[idx]; + *msg_fcn_p = dtap_msg_rr_fcn[idx]; + } + + return; +} +/* This is more or less a copy of the dissect_dtap() code just adding + * L2 Pseudo Length decoding first + * The code should probably be cleaned up. + * The name CCCH might not be correct! + */ +static void +dissect_ccch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + + static gsm_a_tap_rec_t tap_rec[4]; + static gsm_a_tap_rec_t *tap_p; + static guint tap_current=0; + + void (*msg_fcn_p)(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len); + guint8 oct; + guint8 pd; + guint32 offset, saved_offset; + guint32 len; + guint32 oct_1, oct_2; + proto_item *ccch_item = NULL; + proto_tree *ccch_tree = NULL; + proto_item *oct_1_item = NULL; + proto_tree *pd_tree = NULL; + proto_tree *saved_tree = NULL; + const gchar *msg_str; + gint ett_tree; + gint ti; + int hf_idx; + gboolean nsd; + /*guint8 pseudo_len;*/ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + len = tvb_length(tvb); + + if (len < 2){ + /* + * too short to be CCCH + */ + call_dissector(data_handle, tvb, pinfo, tree); + return; + } + + col_append_str(pinfo->cinfo, COL_INFO, "(CCCH) "); + /* + * set tap record pointer + */ + tap_current++; + if (tap_current >= 4) + { + tap_current = 0; + } + tap_p = &tap_rec[tap_current]; + + offset = 0; + oct_2 = 0; + + /* Skip pseeudo hdr here */ + offset = 1; + + /* + * get protocol discriminator + */ + oct_1 = tvb_get_guint8(tvb, offset++); + + if ((((oct_1 & DTAP_TI_MASK) >> 4) & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + /* + * eventhough we don't know if a TI should be in the message yet + * we rely on the TI/SKIP indicator to be 0 to avoid taking this + * octet + */ + oct_2 = tvb_get_guint8(tvb, offset++); + } + + oct = tvb_get_guint8(tvb, offset); + + pd = oct_1 & DTAP_PD_MASK; + ti = -1; + msg_str = NULL; + ett_tree = -1; + hf_idx = -1; + msg_fcn_p = NULL; + nsd = FALSE; + col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ",val_to_str(pd,gsm_a_pd_short_str_vals,"Unknown (%u)")); + + /* + * octet 1 + */ + switch (pd){ + case 6: + get_rr_msg_params(oct, &msg_str, &ett_tree, &hf_idx, &msg_fcn_p); + break; + + default: + /* XXX - hf_idx is still -1! this is a bug in the implementation, and I don't know how to fix it so simple return here */ + return; + } + + /* + * create the protocol tree + */ + if (msg_str == NULL){ + ccch_item = proto_tree_add_protocol_format(tree, proto_a_ccch, tvb, 0, len, + "GSM CCCH - Message Type (0x%02x)", + oct); + + ccch_tree = proto_item_add_subtree(ccch_item, ett_ccch_msg); + }else{ + ccch_item = proto_tree_add_protocol_format(tree, proto_a_ccch, tvb, 0, -1, + "GSM CCCH - %s", msg_str); + + ccch_tree = proto_item_add_subtree(ccch_item, ett_tree); + } + + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", msg_str); + + /* back to the begining */ + saved_offset = offset; + offset = 0; + + curr_offset = offset; + curr_len = len; + len = 1; + + /* L2 Pseudo Length 10.5.2.19 */ + /*pseudo_len = tvb_get_guint8(tvb,offset)>>2;*/ + + saved_tree = tree; + tree = ccch_tree; + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_L2_PSEUDO_LEN, NULL); + tree = saved_tree; + offset = saved_offset; + len = curr_len; + + oct_1_item = + proto_tree_add_text(ccch_tree, + tvb, 1, 1, + "Protocol Discriminator: %s", + val_to_str(pd, protocol_discriminator_vals, "Unknown (%u)")); + + pd_tree = proto_item_add_subtree(oct_1_item, ett_ccch_oct_1); + proto_tree_add_item(pd_tree, hf_gsm_a_L3_protocol_discriminator, tvb, 1, 1, ENC_BIG_ENDIAN); + + if (ti == -1){ + proto_tree_add_item(pd_tree, hf_gsm_a_skip_ind, tvb, 1, 1, ENC_BIG_ENDIAN); + }else{ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x80, 8); + proto_tree_add_text(pd_tree,tvb, 1, 1, + "%s : TI flag: %s", + a_bigbuf, + ((oct_1 & 0x80) ? "allocated by receiver" : "allocated by sender")); + + if ((ti & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + /* ti is extended to next octet */ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x70, 8); + proto_tree_add_text(pd_tree, tvb, 1, 1, + "%s : TIO: The TI value is given by the TIE in octet 2", + a_bigbuf); + }else{ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x70, 8); + proto_tree_add_text(pd_tree,tvb, 1, 1, + "%s : TIO: %u",a_bigbuf,ti & DTAP_TIE_PRES_MASK); + } + } + + + if ((ti != -1) && (ti & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + proto_tree_add_item(tree, hf_gsm_a_extension, tvb, 2, 1, ENC_BIG_ENDIAN); + other_decode_bitfield_value(a_bigbuf, oct_2, DTAP_TIE_MASK, 8); + proto_tree_add_text(pd_tree, tvb, 2, 1, + "%s : TIE: %u", a_bigbuf, oct_2 & DTAP_TIE_MASK); + } + + /* + * N(SD) + */ + if ((pinfo->p2p_dir == P2P_DIR_RECV) && + nsd) + { + /* XXX */ + } + + /* + * add DTAP message name + */ + proto_tree_add_uint_format(ccch_tree, hf_idx, tvb, offset, 1, oct, + "Message Type: %s",msg_str ? msg_str : "(Unknown)"); + + offset++; + + tap_p->pdu_type = GSM_A_PDU_TYPE_DTAP; + tap_p->message_type = (nsd ? (oct & 0x3f) : oct); + tap_p->protocol_disc = pd; + + tap_queue_packet(gsm_a_tap, pinfo, tap_p); + + if (msg_str == NULL) + return; + + if (offset >= len) + return; + + /* + * decode elements + */ + if (msg_fcn_p == NULL){ + proto_tree_add_text(ccch_tree, tvb, offset, len - offset, + "Message Elements"); + }else{ + (*msg_fcn_p)(tvb, ccch_tree, pinfo, offset, len - offset); + } +} + +const value_string gsm_a_rr_short_pd_msg_strings[] = { + { 0x00, "System Information Type 10" }, + { 0x01, "Notification/FACCH" }, + { 0x02, "Uplink Free" }, + { 0x04, "Enhanced Measurement Report" }, + { 0x05, "Measurement Information" }, + { 0x06, "VBS/VGCS Reconfigure" }, + { 0x07, "VBS/VGCS Reconfigure2" }, + { 0x08, "VGCS Additional Information" }, + { 0x09, "VGCS SMS Information" }, + { 0, NULL } +}; + +#define NUM_GSM_SACCH_MSG_RR (sizeof(gsm_a_rr_short_pd_msg_strings)/sizeof(value_string)) +static gint ett_gsm_sacch_msg_rr[NUM_GSM_SACCH_MSG_RR]; +static void (*sacch_msg_rr_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len) = { + NULL, /* System Information Type 10 */ + NULL, /* Notification/FACCH */ + NULL, /* Uplink Free */ + sacch_rr_enh_meas_report, /* Enhanced Measurement Report */ + sacch_rr_meas_info, /* Measurement Information */ + NULL, /* VBS/VGCS Reconfigure */ + NULL, /* VBS/VGCS Reconfigure2 */ + NULL, /* VGCS Additional Information */ + NULL, /* VGCS SMS Information */ + NULL, /* NONE */ +}; + +static void +get_rr_short_pd_msg_params(guint8 mess_type, const gchar **msg_str, int *ett_tree, int *hf_idx, msg_fcn *msg_fcn_p) +{ + gint idx; + + *msg_str = match_strval_idx((guint32) mess_type, gsm_a_rr_short_pd_msg_strings, &idx); + *hf_idx = hf_gsm_a_rr_short_pd_msg_type; + if (*msg_str != NULL) { + *ett_tree = ett_gsm_sacch_msg_rr[idx]; + *msg_fcn_p = sacch_msg_rr_fcn[idx]; + } +} + +const value_string short_protocol_discriminator_vals[] = { + {0x0, "Radio Resources Management messages"}, + { 0, NULL } +}; + +static void +dissect_sacch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + static gsm_a_tap_rec_t tap_rec[4]; + static gsm_a_tap_rec_t *tap_p; + static guint tap_current=0; + + void (*msg_fcn_p)(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len); + guint8 oct, short_pd, mess_type; + guint32 offset, bit_offset = 0; + guint32 len; + proto_item *sacch_item = NULL; + proto_tree *sacch_tree = NULL; + const gchar *msg_str; + gint ett_tree; + int hf_idx; + + len = tvb_length(tvb); + + col_append_str(pinfo->cinfo, COL_INFO, "(SACCH) "); + + /* + * set tap record pointer + */ + tap_current++; + if (tap_current >= 4) + { + tap_current = 0; + } + tap_p = &tap_rec[tap_current]; + + offset = 0; + + oct = tvb_get_guint8(tvb, offset); + + msg_str = NULL; + ett_tree = -1; + hf_idx = -1; + msg_fcn_p = NULL; + + short_pd = (oct & 0x80) >> 7; + mess_type = (oct & 0x7c) >> 2; + + if (short_pd == 0) + { + col_append_fstr(pinfo->cinfo, COL_INFO, "(RR) "); + get_rr_short_pd_msg_params(mess_type, &msg_str, &ett_tree, &hf_idx, &msg_fcn_p); + } + else + { + col_append_fstr(pinfo->cinfo, COL_INFO, "(Unknown) "); + } + + /* + * create the protocol tree + */ + if (msg_str == NULL){ + sacch_item = proto_tree_add_protocol_format(tree, proto_a_sacch, tvb, 0, len, + "GSM SACCH - Message Type (0x%02x)", mess_type); + + sacch_tree = proto_item_add_subtree(sacch_item, ett_sacch_msg); + }else{ + sacch_item = proto_tree_add_protocol_format(tree, proto_a_sacch, tvb, 0, -1, + "GSM SACCH - %s", msg_str); + + sacch_tree = proto_item_add_subtree(sacch_item, ett_tree); + + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", msg_str); + } + + if (short_pd == 0) + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_pd, tvb, offset * 8 + bit_offset++, 1, ENC_BIG_ENDIAN); + + if (hf_idx == -1) + return; + + /* + * add SACCH message name + */ + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_pd_msg_type, tvb, offset * 8 + bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_l2_header, tvb, offset * 8 + bit_offset, 2, ENC_BIG_ENDIAN); + offset++; + + tap_p->pdu_type = GSM_A_PDU_TYPE_SACCH; + tap_p->message_type = mess_type; + tap_p->protocol_disc = short_pd; + + tap_queue_packet(gsm_a_tap, pinfo, tap_p); + + if (msg_str == NULL) + return; + + /* + * decode elements + */ + if (msg_fcn_p == NULL){ + proto_tree_add_text(sacch_tree, tvb, offset, len - offset, + "Message Elements"); + }else{ + (*msg_fcn_p)(tvb, sacch_tree, pinfo, offset, len - offset); + } +} + +/* Register the protocol with Wireshark */ +void +proto_register_gsm_a_rr(void) +{ + guint i; + guint last_offset; + + /* Setup list of header fields */ + + static hf_register_info hf[] = + { + { &hf_gsm_a_dtap_msg_rr_type, + { "DTAP Radio Resources Management Message Type", "gsm_a.dtap_msg_rr_type", + FT_UINT8, BASE_HEX, VALS(gsm_a_dtap_msg_rr_strings), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_elem_id, + { "Element ID", "gsm_a_rr.elem_id", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_gsm_a_bcc, + { "BCC","gsm_a.bcc", + FT_UINT8,BASE_DEC, NULL, 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_ncc, + { "NCC","gsm_a.ncc", + FT_UINT8,BASE_DEC, NULL, 0x38, + NULL, HFILL } + }, + { &hf_gsm_a_bcch_arfcn, + { "BCCH ARFCN(RF channel number)","gsm_a.bcch_arfcn", + FT_UINT16,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_range_nb, + { "Number of Ranges","gsm_a.rr.range_nb", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_range_lower, + { "Range Lower","gsm_a.rr.range_lower", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN used as the lower limit of a range of frequencies to be used by the mobile station in cell selection (Range Lower)", HFILL } + }, + { &hf_gsm_a_rr_range_higher, + { "Range Higher","gsm_a.rr.range_higher", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN used as the higher limit of a range of frequencies to be used by the mobile station in cell selection (Range Higher)", HFILL } + }, + { &hf_gsm_a_rr_ba_list_pref_length, + { "Length of BA List Pref","gsm_a.rr.ba_list_pref_length", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ba_freq, + { "BA Freq","gsm_a.rr.ba_freq", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN indicating a single frequency to be used by the mobile station in cell selection and reselection (BA Freq)", HFILL } + }, + { &hf_gsm_a_rr_utran_freq_list_length, + { "Length of UTRAN freq list","gsm_a.rr.utran_freq_length", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ho_ref_val, + { "Handover reference value","gsm_a.rr.ho_ref_val", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_L2_pseudo_len, + { "L2 Pseudo Length value","gsm_a.rr.rr_2_pseudo_len", + FT_UINT8, BASE_DEC, NULL, 0xfc, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ba_used, + { "BA-USED","gsm_a.rr.ba_used", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dtx_used, + { "DTX-USED","gsm_a.rr.dtx_used", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtx_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_ba_used, + { "3G-BA-USED","gsm_a.rr.3g_ba_used", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_meas_valid, + { "MEAS-VALID","gsm_a.rr.meas_valid", + FT_BOOLEAN,8, TFS(&gsm_a_rr_mv_vals), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_full_serv_cell, + { "RXLEV-FULL-SERVING-CELL","gsm_a.rr.rxlev_full_serv_cell", + FT_UINT8,BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_sub_serv_cell, + { "RXLEV-SUB-SERVING-CELL","gsm_a.rr.rxlev_sub_serv_cell", + FT_UINT8,BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxqual_full_serv_cell, + { "RXQUAL-FULL-SERVING-CELL","gsm_a.rr.rxqual_full_serv_cell", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_rxqual_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxqual_sub_serv_cell, + { "RXQUAL-SUB-SERVING-CELL","gsm_a.rr.rxqual_sub_serv_cell", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_rxqual_vals), 0x0e, + NULL, HFILL } + }, + { &hf_gsm_a_rr_no_ncell_m, + { "NO-NCELL-M","gsm_a.rr.no_ncell_m", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_ncell_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_ncell, + { "RXLEV-NCELL","gsm_a.rr.rxlev_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bcch_freq_ncell, + { "BCCH-FREQ-NCELL","gsm_a.rr.bcch_freq_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bsic_ncell, + { "BSIC-NCELL","gsm_a.rr.bsic_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mobile_time_difference, + { "Mobile Timing Difference value (in half bit periods)","gsm_a.rr.mobile_time_difference", + FT_UINT32,BASE_DEC, NULL, 0xFFFFF8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_atc, + { "ATC","gsm_a.rr.pow_cmd_atc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_atc_value), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_page_mode, + { "Page Mode","gsm_a.rr.page_mode", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_page_mode_vals), 0x0F, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dedicated_mode_or_tbf, + { "Dedicated mode or TBF","gsm_a.rr.dedicated_mode_or_tbf", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_dedicated_mode_or_tbf_vals), 0xF0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_epc, + { "EPC_mode","gsm_a.rr.pow_cmd_epc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_epc_value), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_fpcepc, + { "FPC_EPC","gsm_a.rr.pow_cmd_fpcepc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_fpcepc_value), 0x20, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_powlev, + { "POWER LEVEL","gsm_a.rr.pow_cmd_pow", + FT_UINT8,BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_nci, + { "Normal cell indication(NCI)","gsm_a.rr.sync_ind_nci", + FT_BOOLEAN,8, TFS(&gsm_a_rr_sync_ind_nci_value), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_rot, + { "Report Observed Time Difference(ROT)","gsm_a.rr.sync_ind_rot", + FT_BOOLEAN,8, TFS(&sm_a_rr_sync_ind_rot_value), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_si, + { "Synchronization indication(SI)","gsm_a.rr_sync_ind_si", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_sync_ind_si_vals), 0x03, + NULL, HFILL } + }, + { &hf_gsm_a_rr_format_id, + { "Format Identifier","gsm_a.rr_format_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_freq_list_format_id_vals), 0xce, + NULL, HFILL } + }, + { &hf_gsm_a_rr_format_id2, + { "Format Identifier","gsm_a.rr_format_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_freq_list_format_id_vals), 0x8e, + NULL, HFILL } + }, + { &hf_gsm_a_rr_channel_mode, + { "Channel Mode","gsm_a.rr.channel_mode", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_mode_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_channel_mode2, + { "Channel Mode 2","gsm_a.rr.channel_mode2", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_mode2_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sc, + { "SC","gsm_a.rr.SC", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_sc_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_algorithm_id, + { "Algorithm identifier","gsm_a.algorithm_identifier", + FT_UINT8,BASE_DEC, VALS(gsm_a_algorithm_identifier_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cr, + { "CR","gsm_a.rr.CR", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_cr_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_multirate_speech_ver, + { "Multirate speech version","gsm_a.rr.multirate_speech_ver", + FT_UINT8,BASE_DEC, VALS(multirate_speech_ver_vals), 0xe0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_NCSB, + { "NSCB: Noise Suppression Control Bit","gsm_a.rr.NCSB", + FT_UINT8,BASE_DEC, VALS(NSCB_vals), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ICMI, + { "ICMI: Initial Codec Mode Indicator","gsm_a.rr.ICMI", + FT_UINT8,BASE_DEC, VALS(ICMI_vals), 0x8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_start_mode, + { "Start Mode","gsm_a.rr.start_mode", + FT_UINT8,BASE_DEC, NULL, 0x3, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv, + { "Timing advance value","gsm_a.rr.timing_adv", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_time_diff, + { "Time difference value","gsm_a.rr.time_diff", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tlli, + { "TLLI","gsm_a.rr.tlli", + FT_UINT32,BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tmsi_ptmsi, + { "TMSI/P-TMSI Value","gsm_a.rr.tmsi_ptmsi", + FT_UINT32,BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_target_mode, + { "Target mode","gsm_a.rr.target_mode", + FT_UINT8,BASE_DEC, NULL, 0xc0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_wait_indication, + { "Wait Indication","gsm_a.rr.wait_indication", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_seq_code, + { "Sequence Code","gsm_a.rr.seq_code", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + + { &hf_gsm_a_rr_group_cipher_key_number, + { "Group cipher key number","gsm_a.rr.Group_cipher_key_number", + FT_UINT8,BASE_DEC, NULL, 0x3c, + NULL, HFILL } + }, + { &hf_gsm_a_rr_MBMS_broadcast, + { "MBMS Broadcast","gsm_a.rr.MBMS_broadcast", + FT_BOOLEAN,8, TFS(&gsm_a_rr_MBMS_broadcast_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_MBMS_multicast, + { "MBMS Multicast","gsm_a.rr.MBMS_multicast", + FT_BOOLEAN,8, TFS(&gsm_a_rr_MBMS_multicast_value), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_last_segment, + { "Last Segment","gsm_a.rr.last_segment", + FT_BOOLEAN,8, TFS(&gsm_a_rr_last_segment_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_carrier_ind, + { "Carrier Indication","gsm_a.rr.carrier_ind", + FT_BOOLEAN,8, TFS(&gsm_a_rr_carrier_ind_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ra, + { "Random Access Information (RA)", "gsm_a_rr_ra", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T1prim, + { "T1'", "gsm_a.rr.T1prim", + FT_UINT8, BASE_DEC, NULL, 0xf8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T3, + { "T3", "gsm_a.rr.T3", + FT_UINT16, BASE_DEC, NULL, 0x07e0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T2, + { "T2", "gsm_a.rr.T2", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rfn, + { "RFN", "gsm_a.rr.rfn", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Reduced Frame Number", HFILL } + }, + { &hf_gsm_a_rr_RR_cause, + { "RR cause value","gsm_a.rr.RRcause", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_RR_cause_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cm_cng_msg_req, + { "CLASSMARK CHANGE","gsm_a.rr_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_cm_cng_msg_req, + { "UTRAN CLASSMARK CHANGE","gsm_a.rr_utran_cm_cng_msg_req", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_utran_cm_cng_msg_req_vals), 0x70, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cdma200_cm_cng_msg_req, + { "CDMA2000 CLASSMARK CHANGE","gsm_a.rr_cdma200_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_geran_iu_cm_cng_msg_req, + { "GERAN IU MODE CLASSMARK CHANGE","gsm_a.rr_geran_iu_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch1, + { "Channel 1","gsm_a.rr_chnl_needed_ch1", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch2, + { "Channel 2","gsm_a.rr_chnl_needed_ch2", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch3, + { "Channel 3","gsm_a.rr_chnl_needed_ch3", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch4, + { "Channel 4","gsm_a.rr_chnl_needed_ch4", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_suspension_cause, + { "Suspension cause value","gsm_a.rr.suspension_cause", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_suspension_cause_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_apdu_id, + { "APDU ID","gsm_a.rr.apdu_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_id_vals), 0x0f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_apdu_flags, + { "APDU Flags","gsm_a.rr.apdu_flags", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_flags_vals), 0xf0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8, + { "12,2 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b8", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7, + { "10,2 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b7", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6, + { "7,95 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b6", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x20, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5, + { "7,40 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b5", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4, + { "6,70 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b4", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3, + { "5,90 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b3", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2, + { "5,15 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b2", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1, + { "4,75 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b1", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5, + { "23,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b5", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4, + { "15,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b4", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3, + { "12,65 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b3", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2, + { "8,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b2", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1, + { "6,60 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b1", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_amr_threshold, + { "AMR Threshold", "gsm_a.rr.amr_threshold", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_amr_threshold_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_amr_hysteresis, + { "AMR Hysteresis", "gsm_a.rr.amr_hysteresis", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_amr_hysteresis_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pwrc, + { "PWRC", "gsm_a.rr.pwrc", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Power Control Indicator (PWRC)", HFILL } + }, + { &hf_gsm_a_rr_dtx_bcch, + { "DTX (BCCH)", "gsm_a.rr.dtx_bcch", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_dtx_bcch_vals), 0x30, + "Discontinuous Transmission (DTX-BCCH)", HFILL } + }, + { &hf_gsm_a_rr_dtx_sacch, + { "DTX (SACCH)", "gsm_a.rr.dtx_sacch", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_dtx_sacch_vals), 0xb0, + "Discontinuous Transmission (DTX-SACCH)", HFILL } + }, + { &hf_gsm_a_rr_radio_link_timeout, + { "Radio Link Timeout", "gsm_a.rr.radio_link_timeout", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_radio_link_timeout_vals), 0x0f, + "Radio Link Timeout (s)", HFILL } + }, + { &hf_gsm_a_rr_cell_reselect_hyst, + { "Cell Reselection Hysteresis", "gsm_a.rr.cell_reselect_hyst", + FT_UINT8, BASE_DEC, NULL, 0xe0, + "Cell Reselection Hysteresis (dB)", HFILL } + }, + { &hf_gsm_a_rr_ms_txpwr_max_cch, + { "MS TXPWR MAX CCH", "gsm_a.rr.ms_txpwr_max_cch", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_acs, + { "ACS", "gsm_a.rr.acs", + FT_BOOLEAN, 8, NULL, 0x80, + "Additional Reselect Param Indicator (ACS)", HFILL } + }, + { &hf_gsm_a_rr_neci, + { "NECI", "gsm_a.rr.neci", + FT_UINT8, BASE_DEC, NULL, 0x40, + "New Establishment Cause Indicator (NECI)", HFILL } + }, + { &hf_gsm_a_rr_rxlev_access_min, + { "RXLEV-ACCESS-MIN", "gsm_a.rr.rxlev_access_min", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x3f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mscr, + { "MSCR", "gsm_a.rr.mscr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_mscr_vals), 0x80, + "MSC Release Indicator (MSCR)", HFILL } + }, + { &hf_gsm_a_rr_att, + { "ATT", "gsm_a.rr.att", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_att_vals), 0x40, + "Attach Indicator (ATT)", HFILL } + }, + { &hf_gsm_a_rr_bs_ag_blks_res, + { "BS_AG_BLKS_RES", "gsm_a.rr.bs_ag_blks_res", + FT_UINT8, BASE_DEC, NULL, 0x38, + "Access Grant Reserved Blocks (BS_AG_BLKS_RES)", HFILL } + }, + { &hf_gsm_a_rr_ccch_conf, + { "CCCH-CONF", "gsm_a.rr.ccch_conf", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_ccch_conf_vals), 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cbq3, + { "CBQ3", "gsm_a.rr.cbq3", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cbq3_vals), 0x00, + "Cell Bar Qualify 3", HFILL } + }, + { &hf_gsm_a_rr_bs_pa_mfrms, + { "BS-PA-MFRMS", "gsm_a.rr.bs_pa_mfrms", + FT_UINT8, BASE_DEC, NULL, 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_rr_t3212, + { "T3212", "gsm_a.rr.t3212", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Periodic Update period (T3212) (deci-hours)", HFILL } + }, + { &hf_gsm_a_rr_dyn_arfcn_length, + { "Length of Dynamic Mapping", "gsm_a.rr.dyn_arfcn_length", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gsm_band, + { "GSM Band", "gsm_a.rr.gsm_band", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_band_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn_first, + { "ARFCN First", "gsm_a.rr.arfcn_first", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_band_offset, + { "Band Offset", "gsm_a.rr.band_offset", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn_range, + { "ARFCN Range", "gsm_a.rr.arfcn_range", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_lowest_arfcn, + { "Lowest ARFCN", "gsm_a.rr.lowest_arfcn", + FT_UINT8, BASE_DEC, NULL, 0x7f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_inc_skip_arfcn, + { "Increment skip ARFCN", "gsm_a.rr.inc_skip_arfcn", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gprs_resumption_ack, + { "Ack", "gsm_a.rr.gprs_resumption_ack", + FT_BOOLEAN, 8, TFS(&gsm_a_rr_gprs_resumption_ack_value), 0x01, + "GPRS Resumption Ack bit", HFILL } + }, + { &hf_gsm_a_rr_ext_ind, + { "EXT-IND", "gsm_a.rr.ext_ind", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_ext_ind_vals), 0x20, + "Extension Indication (EXT-IND)", HFILL } + }, + { &hf_gsm_a_rr_ba_ind, + { "BA-IND", "gsm_a.rr.ba_ind", + FT_UINT8, BASE_DEC, NULL, 0x00, + "BCCH Allocation Indication (BA-IND)", HFILL } + }, + { &hf_gsm_a_rr_multiband_reporting, + { "Multiband Reporting", "gsm_a.rr.multiband_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells to be reported in each band if Multiband Reporting", HFILL } + }, + { &hf_gsm_a_rr_ncc_permitted, + { "NCC Permitted", "gsm_a.rr.ncc_permitted", + FT_UINT8, BASE_HEX, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_max_retrans, + { "Max retrans", "gsm_a.rr.max_retrans", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_max_retrans_vals), 0xc0, + "Maximum number of retransmissions", HFILL } + }, + { &hf_gsm_a_rr_tx_integer, + { "Tx-integer", "gsm_a.rr.tx_integer", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_tx_integer_vals), 0x3c, + "Number of Slots to spread Transmission (Tx-integer)", HFILL } + }, + { &hf_gsm_a_rr_cell_barr_access, + { "CELL_BARR_ACCESS", "gsm_a.rr.cell_barr_access", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cell_barr_access_vals), 0x02, + "Cell Barred for Access (CELL_BARR_ACCESS)", HFILL } + }, + { &hf_gsm_a_rr_re, + { "RE", "gsm_a.rr.re", + FT_BOOLEAN, 8, NULL, 0x01, + "Call re-establishment allowed (RE)", HFILL } + }, + { &hf_gsm_a_rr_acc, + { "ACC", "gsm_a.rr.acc", + FT_UINT16, BASE_HEX, NULL, 0xffff, + "Access Control Class N barred (ACC)", HFILL } + }, + { &hf_gsm_a_rr_nch_position, + { "NCH Position", "gsm_a.rr.nch_position", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nch_position_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_mp_change_mark, + { "SI2ter Measurement Parameter Change Mark", "gsm_a.rr.si2ter_mp_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_3g_change_mark, + { "SI2ter 3G Change Mark", "gsm_a.rr.si2ter_3g_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_index, + { "SI2ter Index", "gsm_a.rr.si2ter_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_count, + { "SI2ter Count", "gsm_a.rr.si2ter_count", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_fdd_uarfcn, + { "FDD UARFCN", "gsm_a.rr.fdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bandwidth_fdd, + { "Bandwidth FDD", "gsm_a.rr.bandwidth_fdd", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tdd_uarfcn, + { "TDD UARFCN", "gsm_a.rr.tdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bandwidth_tdd, + { "Bandwidth TDD", "gsm_a.rr.bandwidth_tdd", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn, + { "ARFCN", "gsm_a.rr.arfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "Absolute Radio Frequency Channel Number (ARFCN)", HFILL } + }, + { &hf_gsm_a_rr_bsic, + { "BSIC", "gsm_a.rr.bsic", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Base Station Identify Code (BSIC)", HFILL } + }, + { &hf_gsm_a_rr_qsearch_i, + { "Qsearch I", "gsm_a.rr.qsearch_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level is below (0 7) or above (8 15) threshold (Qsearch I)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qoffset, + { "FDD Qoffset", "gsm_a.rr.fdd_qoffset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xdd_qoffset_vals), 0x00, + "Offset to RLA_C for cell re selection to FDD access technology (FDD Qoffset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qmin, + { "FDD Qmin", "gsm_a.rr.fdd_qmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_qmin_vals), 0x00, + "Minimum threshold for Ec/No for UTRAN FDD cell re-selection (FDD Qmin)", HFILL } + }, + { &hf_gsm_a_rr_tdd_qoffset, + { "TDD Qoffset", "gsm_a.rr.tdd_qoffset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xdd_qoffset_vals), 0x00, + "Offset to RLA_C for cell re selection to TDD access technology (TDD Qoffset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qmin_offset, + { "FDD Qmin Offset", "gsm_a.rr.fdd_qmin_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_qmin_offset_vals), 0x00, + "Offset to FDD Qmin value (FDD Qmin Offset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_rscpmin, + { "FDD RSCPmin", "gsm_a.rr.fdd_rscpmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_rscpmin_vals), 0x00, + "Minimum threshold of RSCP for UTRAN FDD cell re-selection (FDD RSCPmin)", HFILL } + }, + { &hf_gsm_a_rr_3g_ba_ind, + { "3G BA-IND", "gsm_a.rr.3g_ba_ind", + FT_UINT8, BASE_DEC, NULL, 0x00, + "3G BCCH Allocation Indication (3G BA-IND)", HFILL } + }, + { &hf_gsm_a_rr_mp_change_mark, + { "Measurement Parameter Change Mark", "gsm_a.rr.mp_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2quater_index, + { "SI2quater Index", "gsm_a.rr.si2quater_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2quater_count, + { "SI2quater Count", "gsm_a.rr.si2quater_count", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gsm_report_type, + { "Report Type", "gsm_a.rr.gsm_report_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_gsm_report_type_value), 0x0, + "Report type the MS shall use (Report Type)", HFILL } + }, + { &hf_gsm_a_rr_serving_band_reporting, + { "Serving Band Reporting", "gsm_a.rr.serving_band_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells reported from the GSM serving frequency band (Serving Band Reporting)", HFILL } + }, + { &hf_gsm_a_rr_frequency_scrolling, + { "Frequency Scrolling", "gsm_a.rr.frequency_scrolling", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_frequency_scrolling_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rep_priority, + { "Rep Priority", "gsm_a.rr.rep_priority", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_rep_priority_value), 0x0, + "Reporting Priority", HFILL } + }, + { &hf_gsm_a_rr_report_type, + { "Report Type", "gsm_a.rr.report_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_report_type_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reporting_rate, + { "Reporting Rate", "gsm_a.rr.reporting_rate", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_reporting_rate_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_invalid_bsic_reporting, + { "Invalid BSIC Reporting", "gsm_a.rr.invalid_bsic_reporting", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_invalid_bsic_reporting_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_scale_ord, + { "Scale Ord", "gsm_a.rr.scale_ord", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_scale_ord_vals), 0x00, + "Offset used for the reported RXLEV values (Scale Ord)", HFILL } + }, + { &hf_gsm_a_rr_900_reporting_offset, + { "900 Reporting Offset", "gsm_a.rr.900_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 900 (900 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_900_reporting_threshold, + { "900 Reporting Threshold", "gsm_a.rr.900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 900 (900 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_1800_reporting_offset, + { "1800 Reporting Offset", "gsm_a.rr.1800_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 1800 (1800 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_1800_reporting_threshold, + { "1800 Reporting Threshold", "gsm_a.rr.1800_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 1800 (1800 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_400_reporting_offset, + { "400 Reporting Offset", "gsm_a.rr.400_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 400 (400 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_400_reporting_threshold, + { "400 Reporting Threshold", "gsm_a.rr.400_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 400 (400 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_1900_reporting_offset, + { "1900 Reporting Offset", "gsm_a.rr.1900_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 1900 (1900 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_1900_reporting_threshold, + { "1900 Reporting Threshold", "gsm_a.rr.1900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 1900 (1900 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_850_reporting_offset, + { "850 Reporting Offset", "gsm_a.rr.850_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 850 (850 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_850_reporting_threshold, + { "850 Reporting Threshold", "gsm_a.rr.900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 850 (850 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_network_control_order, + { "Network Control Order", "gsm_a.rr.network_control_order", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_network_control_order_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nc_non_drx_period, + { "NC Non DRX Period", "gsm_a.rr.nc_non_drx_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_non_drx_period_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nc_reporting_period_i, + { "NC Reporting Period I", "gsm_a.rr.nc_reporting_period_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_reporting_period_x_vals), 0x00, + "NC Reporting Period in Packet Idle mode (NC Reporting Period I)", HFILL } + }, + { &hf_gsm_a_rr_nc_reporting_period_t, + { "NC Reporting Period T", "gsm_a.rr.nc_reporting_period_t", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_reporting_period_x_vals), 0x00, + "NC Reporting Period in Packet Transfer mode (NC Reporting Period T)", HFILL } + }, + { &hf_gsm_a_rr_index_start_3g, + { "Index Start 3G", "gsm_a.rr.index_start_3g", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_absolute_index_start_emr, + { "Absolute Index Start EMR", "gsm_a.rr.absolute_index_start_emr", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c_initial, + { "QSearch C Initial", "gsm_a.rr.qsearch_c_initial", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_qsearch_c_initial_value), 0x0, + "Qsearch value to be used in connected mode before Qsearch C is received (QSearch C Initial)", HFILL } + }, + { &hf_gsm_a_rr_fdd_rep_quant, + { "FDD Rep Quant", "gsm_a.rr.fdd_rep_quant", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_fdd_rep_quant_value), 0x0, + "FDD Reporting Quantity (FDD Rep Quant)", HFILL } + }, + { &hf_gsm_a_rr_fdd_multirat_reporting, + { "FDD Multirat Reporting", "gsm_a.rr.fdd_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells from the FDD access technology that shall be included in the list of strongest cells or in the measurement report (FDD Multirat Reporting)", HFILL } + }, + { &hf_gsm_a_rr_tdd_multirat_reporting, + { "TDD Multirat Reporting", "gsm_a.rr.tdd_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells from the TDD access technology that shall be included in the list of strongest cells or in the measurement report (TDD Multirat Reporting)", HFILL } + }, + { &hf_gsm_a_rr_qsearch_p, + { "Qsearch P", "gsm_a.rr.qsearch_p", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level below threshold (Qsearch P)", HFILL } + }, + { &hf_gsm_a_rr_3g_search_prio, + { "3G Search Prio (ignored in Rel-8)", "gsm_a.rr.3g_search_prio", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_3g_search_prio_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_offset, + { "FDD Reporting Offset", "gsm_a.rr.fdd_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for FDD access technology (FDD Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_rscp, + { "FDD Reporting Threshold RSCP", "gsm_a.rr.fdd_reporting_threshold_rscp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_rscp_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for FDD access technology (FDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_ecn0, + { "FDD Reporting Threshold EcN0", "gsm_a.rr.fdd_reporting_threshold_ecn0", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_ecn0_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for FDD access technology (FDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_offset, + { "TDD Reporting Offset", "gsm_a.rr.tdd_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for TDD access technology (TDD Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_threshold_rscp, + { "TDD Reporting Threshold RSCP", "gsm_a.rr.tdd_reporting_threshold_rscp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_rscp_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for TDD access technology (TDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_threshold_ecn0, + { "TDD Reporting Threshold EcN0", "gsm_a.rr.tdd_reporting_threshold_ecn0", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_ecn0_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for TDD access technology (TDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_2, + { "FDD Reporting Threshold 2", "gsm_a.rr.fdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Reporting threshold for the CPICH parameter (Ec/No or RSCP) that is not reported according to FDD_REP_QUANT (FDD Reporting Threshold 2)", HFILL } + }, + { &hf_gsm_a_rr_3g_ccn_active, + { "3G CCN Active", "gsm_a.rr.3g_ccn_active", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_3g_ccn_active_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_700_reporting_offset, + { "700 Reporting Offset", "gsm_a.rr.700_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 700 (700 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_700_reporting_threshold, + { "700 Reporting Threshold", "gsm_a.rr.700_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 700 (700 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_810_reporting_offset, + { "810 Reporting Offset", "gsm_a.rr.810_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 810 (810 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_810_reporting_threshold, + { "810 Reporting Threshold", "gsm_a.rr.810_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 810 (810 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_cbq, + { "CBQ", "gsm_a.rr.cbq", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Cell Bar Qualify", HFILL } + }, + { &hf_gsm_a_rr_cell_reselect_offset, + { "Cell Reselect Offset", "gsm_a.rr.cell_reselect_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cell_reselect_offset_vals), 0x00, + "Offset to the C2 reselection criterion (Cell Reselect Offset)", HFILL } + }, + { &hf_gsm_a_rr_temporary_offset, + { "Temporary Offset", "gsm_a.rr.temporary_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_temporary_offset_vals), 0x0, + "Negative offset to C2 for the duration of Penalty Time (Temporary Offset)", HFILL } + }, + { &hf_gsm_a_rr_penalty_time, + { "Penalty Time", "gsm_a.rr.penalty_time", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_penalty_time_vals), 0x0, + "Duration for which the temporary offset is applied (Penalty Time)", HFILL } + }, + { &hf_gsm_a_rr_gprs_ra_colour, + { "GPRS RA Colour", "gsm_a.rr.gprs_ra_colour", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13_position, + { "SI13 Position", "gsm_a.rr.si13_position", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si13_position_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_power_offset, + { "Power Offset", "gsm_a.rr.power_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_power_offset_vals), 0x0, + "Power offset used in conjunction with the MS TXPWR MAX CCH parameter by the class 3 DCS 1800 MS (Power Offset)", HFILL } + }, + { &hf_gsm_a_rr_si2quater_position, + { "SI2quater Position", "gsm_a.rr.si2quater_position", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si2quater_position_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13alt_position, + { "SI13alt Position", "gsm_a.rr.si13alt_position", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si13alt_position_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_prio_thr, + { "Prio Thr", "gsm_a.rr.prio_thr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_prio_thr_vals), 0x0, + "Prio signal strength threshold is related to RXLEV ACCESS_MIN (Prio Thr)", HFILL } + }, + { &hf_gsm_a_rr_lsa_offset, + { "LSA Offset", "gsm_a.rr.lsa_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lsa_offset_vals), 0x0, + "Offset to be used for LSA cell re selection between cells with the same LSA priorities (LSA Offset)", HFILL } + }, + { &hf_gsm_a_rr_paging_channel_restructuring, + { "Paging Channel Restructuring", "gsm_a.rr.paging_channel_restructuring", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_paging_channel_restructuring_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_sacch, + { "NLN (SACCH)", "gsm_a.rr.nln_sacch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_status_sacch, + { "NLN Status (SACCH)", "gsm_a.rr.nln_status_sacch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_pch, + { "NLN (PCH)", "gsm_a.rr.nln_pch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_status_pch, + { "NLN Status (PCH)", "gsm_a.rr.nln_status_pch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_vbs_vgcs_inband_notifications, + { "Inband Notifications", "gsm_a.rr.vbs_vgcs_inband_notifications", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_vbs_vgcs_inband_notifications_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_vbs_vgcs_inband_pagings, + { "Inband Pagings", "gsm_a.rr.vbs_vgcs_inband_pagings", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_vbs_vgcs_inband_pagings_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rac, + { "RAC", "gsm_a.rr.rac", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Routeing Area Code", HFILL } + }, + { &hf_gsm_a_rr_max_lapdm, + { "Max LAPDm", "gsm_a.rr.max_lapdm", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_max_lapdm_vals), 0x0, + "Maximum number of LAPDm frames on which a layer 3 can be segmented into and be sent on the main DCCH (Max LAPDm)", HFILL } + }, + { &hf_gsm_a_rr_gprs_ms_txpwr_max_ccch, + { "GPRS MS TxPwr Max CCH", "gsm_a.rr.gprs_ms_txpwr_max_cch", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dedicated_mode_mbms_notification_support, + { "Dedicated Mode MBMS Notification Support", "gsm_a.rr.dedicated_mode_mbms_notification_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dedicated_mode_mbms_notification_support_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mnci_support, + { "MNCI Support", "gsm_a.rr.mnci_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_mnci_support_value), 0x0, + "MBMS Neighbouring Cell Information Support (MNCI Support)", HFILL } + }, + { &hf_gsm_a_rr_amr_config, + { "AMR Configuration", "gsm_a.rr.amr_config", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bcch_change_mark, + { "BCCH Change Mark", "gsm_a.rr.bcch_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si_change_field, + { "SI Change Field", "gsm_a.rr.si_change_field", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si_change_field_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13_change_mark, + { "SI13 Change Mark", "gsm_a.rr.si13_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_hsn, + { "HSN", "gsm_a.rr.hsn", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Hopping Sequence Number (HSN)", HFILL } + }, + { &hf_gsm_a_rr_rfl_number, + { "RFL Number", "gsm_a.rr.rfl_number", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Radio Frequency List Number (RFL Number)", HFILL } + }, + { &hf_gsm_a_rr_arfcn_index, + { "ARFCN Index", "gsm_a.rr.arfcn_index", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ma_length, + { "MA Length", "gsm_a.rr.ma_length", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Mobile Allocation Length (MA Length)", HFILL } + }, + { &hf_gsm_a_rr_psi1_repeat_period, + { "PSI1 Repeat Period", "gsm_a.rr.psi1_repeat_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_psi1_repeat_period_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pbcch_pb, + { "Pb", "gsm_a.rr.pbcch_pb", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pbcch_pb_vals), 0x0, + "Power reduction on PBCCH/PCCCH (Pb)", HFILL } + }, + { &hf_gsm_a_rr_pbcch_tsc, + { "TSC", "gsm_a.rr.pbcch_tsc", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Training Sequence Code for PBCCH (TSC)", HFILL } + }, + { &hf_gsm_a_rr_pbcch_tn, + { "TN", "gsm_a.rr.pbcch_tn", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Timeslot Number for PCCH (TN)", HFILL } + }, + { &hf_gsm_a_rr_spgc_ccch_sup, + { "SPGC CCCH Sup", "gsm_a.rr.spgc_ccch_sup", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_spgc_ccch_sup_value), 0x0, + "Split PG Cycle Code on CCCH Support (SPGC CCCH Sup)", HFILL } + }, + { &hf_gsm_a_rr_priority_access_thr, + { "Priority Access Thr", "gsm_a.rr.priority_access_thr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_priority_access_thr_vals), 0x0, + "Priority Access Threshold for packet access (Priority Access Thr)", HFILL } + }, + { &hf_gsm_a_rr_nmo, + { "NMO", "gsm_a.rr.nmo", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nmo_vals), 0x0, + "Network mode of Operation (NMO)", HFILL } + }, + { &hf_gsm_a_rr_t3168, + { "T3168", "gsm_a.rr.t3168", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t3168_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_t3192, + { "T3192", "gsm_a.rr.t3192", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t3192_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_drx_timer_max, + { "DRX Timer Max", "gsm_a.rr.drx_timer_max", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_drx_timer_max_vals), 0x0, + "Discontinuous Reception Timer Max (DRX Timer Max)", HFILL } + }, + { &hf_gsm_a_rr_access_burst_type, + { "Access Burst Type", "gsm_a.rr.access_burst_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_access_burst_type_value), 0x0, + "Format used in the PACKET CHANNEL REQUEST message, the PS HANDOVER ACCESS message, the PTCCH uplink block and in the PACKET CONTROL ACKNOWLEDGMENT message (Access Burst Type)", HFILL } + }, + { &hf_gsm_a_rr_control_ack_type, + { "Control Ack Type", "gsm_a.rr.control_ack_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_control_ack_type_value), 0x0, + "Default format of the PACKET CONTROL ACKNOWLEDGMENT message (Control Ack Type)", HFILL } + }, + { &hf_gsm_a_rr_bs_cv_max, + { "BS CV Max", "gsm_a.rr.bs_cv_max", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Base Station Countdown Value Maximum (BS CV Max)", HFILL } + }, + { &hf_gsm_a_rr_pan_dec, + { "PAN Dec", "gsm_a.rr.pan_dec", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pan_inc, + { "PAN Inc", "gsm_a.rr.pan_inc", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pan_max, + { "PAN Max", "gsm_a.rr.pan_max", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pan_max_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_packet_channel_request, + { "EGPRS Packet Channel Request", "gsm_a.rr.egprs_packet_channel_request", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_egprs_packet_channel_request_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bep_period, + { "BEP Period", "gsm_a.rr.bep_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_bep_period_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pfc_feature_mode, + { "PFC Feature Mode", "gsm_a.rr.pfc_feature_mode", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_pfc_feature_mode_value), 0x0, + "Packet Flow Context Feature Mode (PFC Feature Mode)", HFILL } + }, + { &hf_gsm_a_rr_dtm_support, + { "DTM Support", "gsm_a.rr.dtm_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtm_support_value), 0x0, + "Dual Transfer Mode Support (DTM Support)", HFILL } + }, + { &hf_gsm_a_rr_bss_paging_coordination, + { "BSS Paging Coordination", "gsm_a.rr.bss_paging_coordination", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_bss_paging_coordination_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ccn_active, + { "CCN Active", "gsm_a.rr.ccn_active", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_ccn_active_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nw_ext_utbf, + { "NW Ext UTBF", "gsm_a.rr.nw_ext_utbf", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_nw_ext_utbf_value), 0x0, + "Network Extended Uplink TBF (NW Ext UTBF)", HFILL } + }, + { &hf_gsm_a_rr_multiple_tbf_capability, + { "Multiple TBF Capability", "gsm_a.rr.multiple_tbf_capability", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_multiple_tbf_capability_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ext_utbf_no_data, + { "Ext UTBF No Data", "gsm_a.rr.ext_utbf_no_data", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_ext_utbf_no_data_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dtm_enhancements_capability, + { "DTM Enhancements Capability", "gsm_a.rr.dtm_enhancements_capability", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtm_enhancements_capability_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reduced_latency_access, + { "Reduced Latency Access", "gsm_a.rr.reduced_latency_access", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_reduced_latency_access_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_alpha, + { "Alpha", "gsm_a.rr.alpha", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_alpha_vals), 0x0, + "Alpha parameter for GPR MS output power control (Alpha)", HFILL } + }, + { &hf_gsm_a_rr_t_avg_w, + { "T Avg W", "gsm_a.rr.t_avg_w", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t_avg_x_vals), 0x0, + "Signal strength filter period for power control in packet idle mode", HFILL } + }, + { &hf_gsm_a_rr_t_avg_t, + { "T Avg T", "gsm_a.rr.t_avg_t", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t_avg_x_vals), 0x0, + "Signal strength filter period for power control in packet transfer mode", HFILL } + }, + { &hf_gsm_a_rr_pc_meas_chan, + { "PC Meas Chan", "gsm_a.rr.pc_meas_chan", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_pc_meas_chan_value), 0x0, + "Channel used to measure the received power level on the downlink for the purpose of the uplink power control (PC Meas Chan)", HFILL } + }, + { &hf_gsm_a_rr_n_avg_i, + { "N Avg I", "gsm_a.rr.n_avg_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_n_avg_i_vals), 0x0, + "Interfering signal strength filter constant for power control (N Avg I)", HFILL } + }, + { &hf_gsm_a_rr_sgsnr, + { "SGSNR", "gsm_a.rr.sgsnr", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_sgsnr_value), 0x0, + "SGSN Release (SGSNR)", HFILL } + }, + { &hf_gsm_a_rr_si_status_ind, + { "SI Status Ind", "gsm_a.rr.si_status_ind", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si_status_ind_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_lb_ms_txpwr_max_cch, + { "LB MS TxPwr Max CCCH", "gsm_a.rr.n_avg_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_n_avg_i_vals), 0x0, + "Maximum TX power level an MS is allowed to use on all other than DCS 1800 and PCS 1900 frequency bands when accessing the system until otherwise commanded (LB MS TxPwr Max CCCH)", HFILL } + }, + { &hf_gsm_a_rr_si2n_support, + { "SI2n Support", "gsm_a.rr.si2n_support", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si2n_support_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mi_index, + { "Measurement Information Index", "gsm_a.rr.mi_index", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mi_count, + { "Measurement Information Count", "gsm_a.rr.mi_count", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_wait, + { "3G Wait", "gsm_a.rr.3g_wait", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_3g_wait_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c, + { "Qsearch C", "gsm_a.rr.qsearch_c", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level is below (0 7) or above (8 15) threshold (Qsearch C)", HFILL } + }, + { &hf_gsm_a_rr_bsic_seen, + { "BSIC Seen", "gsm_a.rr.bsic_seen", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_bsic_seen_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_scale, + { "Scale", "gsm_a.rr.scale", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_scale_value), 0x0, + "Offset applied for the reported RXLEV values (Scale)", HFILL } + }, + { &hf_gsm_a_rr_mean_bep_gmsk, + { "Mean BEP GMSK", "gsm_a.rr.mean_bep_gmsk", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_mean_bep_gmsk_vals), 0x00, + "Mean Bit Error Probability in GMSK (Mean BEP GMSK)", HFILL } + }, + { &hf_gsm_a_rr_mean_cv_bep, + { "CV BEP", "gsm_a.rr.cv_bep", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cv_bep_vals), 0x00, + "Coefficient of Variation of the Bit Error Probability (CV BEP)", HFILL } + }, + { &hf_gsm_a_rr_nbr_rcvd_blocks, + { "Nb Rcvd Blocks", "gsm_a.rr.nbr_rcvd_blocks", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of correctly decoded blocks that were completed during the measurement report period (Nb Rcvd Blocks)", HFILL } + }, + { &hf_gsm_a_rr_reporting_quantity, + { "Reporting Quantity", "gsm_a.rr.reporting_quantity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_utran_start, + { "UTRAN Start", "gsm_a.rr.3g_priority.utran_start", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_start), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_utran_stop, + { "UTRAN Stop", "gsm_a.rr.3g_priority.utran_stop", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_stop), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio, + { "DEFAULT_UTRAN_PRIORITY", "gsm_a.rr.3g_priority.default_utran_prio", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran, + { "DEFAULT_THRESH_UTRAN", "gsm_a.rr.3g_priority.default_threshold_utran", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin, + { "DEFAULT_UTRAN_QRXLEVMIN", "gsm_a.rr.3g_priority.default_utran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_utran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_frequency_index, + { "UTRAN Frequency Index", "gsm_a.rr.3g_priority.utran_frequency_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_priority, + { "UTRAN_PRIORITY", "gsm_a.rr.3g_priority.utran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_utran_high, + { "THRESH_UTRAN_high", "gsm_a.rr.3g_priority.thres_utran_high", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_utran_low, + { "THRESH_UTRAN_low", "gsm_a.rr.3g_priority.thres_utran_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_qrxlevmin, + { "UTRAN_QRXLEVMIN", "gsm_a.rr.3g_priority.utran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_utran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_ccn_active, + { "E-UTRAN_CCN_ACTIVE", "gsm_a.rr.3g_priority.eutran_ccn_active", + FT_BOOLEAN, BASE_DEC, TFS(&eutran_ccn_active), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_start, + { "E-UTRAN Start", "gsm_a.rr.3g_priority.eutran_start", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_start), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_stop, + { "E-UTRAN Stop", "gsm_a.rr.3g_priority.utran_stop", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_stop), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c_eutran_initial, + { "Qsearch_C_E-UTRAN_Initial", "gsm_a.rr.qsearch_c_eutran_initial", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_c_eutran_initial), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_rep_quant, + { "E-UTRAN_REP_QUANT", "gsm_a.rr.eutran_rep_quant", + FT_BOOLEAN, BASE_DEC, TFS(&eutran_rep_quant), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_multirat_reporting, + { "E-UTRAN_MULTIRAT_REPORTING (nb of E-UTRAN cells to be included in measurement report)", "gsm_a.rr.eutran_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, + { "E-UTRAN_FDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_fdd_reporting_threshold_rsrp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrp_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, + { "E-UTRAN_FDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_fdd_reporting_threshold_rsrq", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrq_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, + { "E-UTRAN_FDD_REPORTING_THRESHOLD_2", "gsm_a.rr.eutran_fdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_offset, + { "E-UTRAN_FDD_REPORTING_OFFSET", "gsm_a.rr.eutran_fdd_reporting_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, + { "E-UTRAN_TDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_tdd_reporting_threshold_rsrp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrp_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, + { "E-UTRAN_TDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_tdd_reporting_threshold_rsrq", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrq_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, + { "E-UTRAN_TDD_REPORTING_THRESHOLD_2", "gsm_a.rr.eutran_tdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_offset, + { "E-UTRAN_TDD_REPORTING_OFFSET", "gsm_a.rr.eutran_tdd_reporting_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_measurement_report_offset, + { "E-UTRAN_FDD_MEASUREMENT_REPORT_OFFSET", "gsm_a.rr.eutran_fdd_measurement_report_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_measurement_report_offset, + { "E-UTRAN_TDD_MEASUREMENT_REPORT_OFFSET", "gsm_a.rr.eutran_tdd_measurement_report_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reporting_granularity, + { "REPORTING_GRANULARITY", "gsm_a.rr.reporting_granularity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_default_measurement_control_eutran, + { "DEFAULT_Measurement_Control_E-UTRAN", "gsm_a.rr.eutran_default_measurement_control_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_measurement_control_eutran, + { "Measurement_Control_E-UTRAN", "gsm_a.rr.eutran_measurement_control_eutran", + FT_UINT8, BASE_DEC, TFS(&measurement_control_eutran), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_p_eutran, + { "Qsearch_P_E-UTRAN", "gsm_a.rr.qsearch_p_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_geran_priority, + { "GERAN_PRIORITY", "gsm_a.rr.serving_cell_priority_param_geran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search, + { "THRESH_Priority_Search", "gsm_a.rr.serving_cell_priority_param_thresh_prio_search", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_thresh_priority_search), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low, + { "THRESH_GSM_low", "gsm_a.rr.serving_cell_priority_param_thresh_gsm_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_thresh_gsm_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_h_prio, + { "H_PRIO", "gsm_a.rr.serving_cell_priority_param_h_prio", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_priority_param_h_prio), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_t_reselection, + { "T_Reselection", "gsm_a.rr.serving_cell_priority_param_t_reselection", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_priority_param_t_reselection), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_earfcn, + { "EARFCN", "gsm_a.rr.earfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_measurement_bandwidth, + { "Measurement Bandwidth", "gsm_a.rr.eutran_measurement_bandwidth", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_eutran_measurement_bandwidth), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_priority, + { "E-UTRAN_PRIORITY", "gsm_a.rr.eutran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_eutran_high, + { "THRESH_EUTRAN_high", "gsm_a.rr.thresh_eutran_high", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_eutran_low, + { "THRESH_EUTRAN_low", "gsm_a.rr.thresh_eutran_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_qrxlevmin, + { "E-UTRAN_QRXLEVMIN", "gsm_a.rr.eutran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_eutran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid, + { "PCID", "gsm_a.rr.pcid", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_bitmap_group, + { "PCID_BITMAP_GROUP", "gsm_a.rr.pcid_bitmap_group", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern_length, + { "PCID_Pattern_length", "gsm_a.rr.pcid_pattern_length", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pcid_psc_pattern_length), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern, + { "PCID_Pattern", "gsm_a.rr.pcid_pattern", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern_sense, + { "PCID_pattern_sense", "gsm_a.rr.pcid_pattern_sense", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_rr_pcid_pattern_sense), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_frequency_index, + { "E-UTRAN_FREQUENCY_INDEX", "gsm_a.rr.eutran_frequency_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_psc, + { "PSC", "gsm_a.rr.psc", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_psc_pattern_length, + { "PSC_Pattern_length", "gsm_a.rr.psc_pattern_length", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pcid_psc_pattern_length), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_psc_pattern_sense, + { "PSC_pattern_sense", "gsm_a.rr.psc_pattern_sense", + FT_BOOLEAN, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_csg_fdd_uarfcn, + { "CSG FDD UARFCN", "gsm_a.rr.utran_csg_fdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_csg_tdd_uarfcn, + { "CSG TDD UARFCN", "gsm_a.rr.utran_csg_tdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_csg_earfcn, + { "CSG_EARFCN", "gsm_a.rr.csg_earfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, + { "PCID_pattern_sense", "gsm_a.rr._meas_ctrl_utran", + FT_BOOLEAN, BASE_DEC, TFS(&measurement_control_utran), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_extended_ra, + { "Extended_RA", "gsm_a.rr.extended_ra", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_access_tech_type, + { "Access_Technology_Type", "gsm_a.rr.access_tech_type", + FT_UINT8, BASE_DEC, VALS(gsm_a_access_tech_type_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tfi_assignment, + { "TFI_Assignment", "gsm_a.rr.tfi_assignment", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_polling, + { "Polling", "gsm_a.rr.polling", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_polling_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_usf, + { "USF", "gsm_a.rr.usf", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_usf_granularity, + { "USF_granularity", "gsm_a.rr.usf_granularity", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_usf_granularity_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_p0, + { "P0", "gsm_a.rr.p0", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pr_mode, + { "pr_mode", "gsm_a.rr.pr_mode", + FT_BOOLEAN, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_mcs, + { "Egprs_Modulation_and_Coding_Scheme", "gsm_a.rr.egprs_mcs", + FT_UINT8, BASE_DEC, VALS(gsm_a_egprs_mcs_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tlli_block_channel_coding, + { "TLLI_Block_Channel_Coding", "gsm_a.rr.tlli_block_cs", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_tlli_block_channel_coding_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bep_period2, + { "Bep_Period2", "gsm_a.rr.bep_period2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_resegment, + { "Resegment", "gsm_a.rr.resegment", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_resegment_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_window_size, + { "Egprs_Windows_Size", "gsm_a.rr.egprs_win_size", + FT_UINT8, BASE_DEC, VALS(gsm_a_egprs_windows_size_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gamma, + { "Gamma", "gsm_a.rr.gamma", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gamma_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv_index, + { "Timing_Advance_Index", "gsm_a.rr.timing_adv_idx", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tbf_starting_time, + { "TBF_Starting_Time", "gsm_a.rr.tvf_start_time", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_num_of_radio_block_allocated, + { "Number_of_Radio_Block_Allocated", "gsm_a.rr.num_of_radio_blk_allocated", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pfi, + { "PFI", "gsm_a.rr.pfi", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mbms_service_id, + { "MBMS_Service_ID", "gsm_a.rr.mbms_service_id", + FT_UINT32, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ms_id, + { "MS_ID", "gsm_a.rr.ms_id", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv_timeslot_num, + { "Timing_Advance_Timeslot_Number", "gsm_a.rr.timing_adv_ts", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gprs_cs, + { "Channel_Coding_Command", "gsm_a.rr.gprs_cs", + FT_UINT8, BASE_DEC, VALS(gsm_a_gprs_cs_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rlc_mode, + { "RLC_Mode", "gsm_a.rr.rlc_mode", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_rlc_mode_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ta_valid, + { "TA_Valid", "gsm_a.rr.ta_valid", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_ta_valid_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_link_quality_meas_mode, + { "Link_Quality_Measure_Mode", "gsm_a.rr.link_qual_meas_mode", + FT_UINT8, BASE_DEC, VALS(&gsm_a_link_quality_meas_mode_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_emr_bitmap_length, + { "BITMAP_LENGTH", "gsm_a.rr.emr_bitmap_len", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_n_eutran, + { "N_E-UTRAN", "gsm_a.rr.eutran_mr_n_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_freq_idx, + { "E-UTRAN_FREQUENCY_INDEX", "gsm_a.rr.eutran_mr_freq_idx", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_cell_id, + { "CELL IDENTITY", "gsm_a.rr.eutran_mr_cell_id", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_rpt_quantity, + { "REPORTING_QUANTITY", "gsm_a.rr.eutran_mr_rpt_quantity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ma_channel_set, + { "Channel Set", "gsm_a.rr.ma_channel_set", + FT_UINT8, BASE_HEX, NULL, 0x00, + NULL, HFILL } + }, + { &hf_n_range_orig_arfcn, + { "ORIG-ARFCN", "gsm_a.rr.orig_arfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + }; + + static hf_register_info hf_rr_short_pd[] = + { + { &hf_gsm_a_rr_short_pd, + { "Radio Resources Short Protocol Discriminator", "gsm_a.rr_short_pd", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_short_pd_msg_type, + { "Radio Resources Short PD Message Type", "gsm_a.rr_short_pd_type", + FT_UINT8, BASE_HEX, VALS(gsm_a_rr_short_pd_msg_strings), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_short_l2_header, + { "Radio Resources Short L2 Header", "gsm_a.rr_short_l2_header", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL } + } + }; + + /* Setup protocol subtree array */ +#define NUM_INDIVIDUAL_ELEMS 3 + gint *ett[NUM_INDIVIDUAL_ELEMS + + NUM_GSM_DTAP_MSG_RR + + NUM_GSM_RR_ELEM + + NUM_GSM_RR_REST_OCTETS_ELEM + + NUM_GSM_SACCH_MSG_RR]; + + ett[0] = &ett_ccch_msg; + ett[1] = &ett_ccch_oct_1; + ett[2] = &ett_sacch_msg; + + last_offset = NUM_INDIVIDUAL_ELEMS; + + for (i=0; i < NUM_GSM_DTAP_MSG_RR; i++, last_offset++) + { + ett_gsm_dtap_msg_rr[i] = -1; + ett[last_offset] = &ett_gsm_dtap_msg_rr[i]; + } + + for (i=0; i < NUM_GSM_RR_ELEM; i++, last_offset++) + { + ett_gsm_rr_elem[i] = -1; + ett[last_offset] = &ett_gsm_rr_elem[i]; + } + + for (i=0; i < NUM_GSM_RR_REST_OCTETS_ELEM; i++, last_offset++) + { + ett_gsm_rr_rest_octets_elem[i] = -1; + ett[last_offset] = &ett_gsm_rr_rest_octets_elem[i]; + } + + for (i=0; i < NUM_GSM_SACCH_MSG_RR; i++, last_offset++) + { + ett_gsm_sacch_msg_rr[i] = -1; + ett[last_offset] = &ett_gsm_sacch_msg_rr[i]; + } + + /* Register the protocol name and description */ + proto_a_ccch = + proto_register_protocol("GSM CCCH", "GSM CCCH", "gsm_a_ccch"); + + proto_register_field_array(proto_a_ccch, hf, array_length(hf)); + + /* subdissector code */ + register_dissector("gsm_a_ccch", dissect_ccch, proto_a_ccch); + + /* Register the protocol name and description */ + proto_a_sacch = + proto_register_protocol("GSM SACCH", "GSM SACCH", "gsm_a_sacch"); + + proto_register_field_array(proto_a_sacch, hf_rr_short_pd, array_length(hf_rr_short_pd)); + + /* subdissector code */ + register_dissector("gsm_a_sacch", dissect_sacch, proto_a_sacch); + + /* subtree array (for both sub-dissectors) */ + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_gsm_a_rr(void) +{ + data_handle = find_dissector("data"); + rrc_irat_ho_info_handle = find_dissector("rrc.irat.irat_ho_info"); + rrc_irat_ho_to_utran_cmd_handle = find_dissector("rrc.irat.ho_to_utran_cmd"); + rrlp_dissector = find_dissector("rrlp"); +} diff --git a/src/main/resource/testFiles/wireshark/revFiles/87646b_2cc0fb_epan#proto.c b/src/main/resource/testFiles/wireshark/revFiles/87646b_2cc0fb_epan#proto.c new file mode 100644 index 0000000..a6b0325 --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/87646b_2cc0fb_epan#proto.c @@ -0,0 +1,7440 @@ +/* proto.c + * Routines for protocol tree + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "packet.h" +#include "ptvcursor.h" +#include "strutil.h" +#include "addr_resolv.h" +#include "oids.h" +#include "plugins.h" +#include "proto.h" +#include "epan_dissect.h" +#include "tvbuff.h" +#include "emem.h" +#include "charsets.h" +#include "asm_utils.h" +#include "column-utils.h" +#include "to_str.h" +#include "expert.h" +#include "show_exception.h" + +#include "wspython/wspy_register.h" + +#define SUBTREE_ONCE_ALLOCATION_NUMBER 8 +#define SUBTREE_MAX_LEVELS 256 +/* Throw an exception if we exceed this many tree items. */ +/* XXX - This should probably be a preference */ +#define MAX_TREE_ITEMS (1 * 1000 * 1000) + + +typedef struct __subtree_lvl { + gint cursor_offset; + proto_item *it; + proto_tree *tree; +} subtree_lvl; + +struct ptvcursor { + subtree_lvl *pushed_tree; + guint8 pushed_tree_index; + guint8 pushed_tree_max; + proto_tree *tree; + tvbuff_t *tvb; + gint offset; +}; + +/* Candidates for assembler */ +static int +wrs_count_bitshift(const guint32 bitmask) +{ + int bitshift = 0; + + while ((bitmask & (1 << bitshift)) == 0) + bitshift++; + return bitshift; +} + +#define cVALS(x) (const value_string*)(x) + +/** See inlined comments. + @param tree the tree to append this item to + @param hfindex field index + @param hfinfo header_field + @return the header field matching 'hfinfo' */ +#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \ + /* If this item is not referenced we dont have to do much work \ + at all but we should still return a node so that field items \ + below this node (think proto_item_add_subtree()) will still \ + have somewhere to attach to or else filtering will not work \ + (they would be ignored since tree would be NULL). \ + DONT try to fake a node where PTREE_FINFO(tree) is NULL \ + since dissectors that want to do proto_item_set_len() or \ + other operations that dereference this would crash. \ + We fake FT_PROTOCOL unless some clients have requested us \ + not to do so. \ + */ \ + if (!tree) \ + return NULL; \ + PTREE_DATA(tree)->count++; \ + if (PTREE_DATA(tree)->count > MAX_TREE_ITEMS) { \ + if (getenv("WIRESHARK_ABORT_ON_TOO_MANY_ITEMS") != NULL) \ + g_error("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS); \ + /* Let the exception handler add items to the tree */ \ + PTREE_DATA(tree)->count = 0; \ + THROW_MESSAGE(DissectorError, \ + ep_strdup_printf("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS)); \ + } \ + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); \ + if (!(PTREE_DATA(tree)->visible)) { \ + if (PTREE_FINFO(tree)) { \ + if ((hfinfo->ref_type != HF_REF_TYPE_DIRECT) \ + && (hfinfo->type != FT_PROTOCOL || \ + PTREE_DATA(tree)->fake_protocols)) { \ + /* just return tree back to the caller */\ + return tree; \ + } \ + } \ + } + +/** See inlined comments. + @param pi the created protocol item we're about to return */ +#define TRY_TO_FAKE_THIS_REPR(pi) \ + g_assert(pi); \ + if (!(PTREE_DATA(pi)->visible)) { \ + /* If the tree (GUI) isn't visible it's pointless for us to generate the protocol \ + * items string representation */ \ + return pi; \ + } + +static const char *hf_try_val_to_str(guint32 value, const header_field_info *hfinfo); + +static void fill_label_boolean(field_info *fi, gchar *label_str); +static void fill_label_bitfield(field_info *fi, gchar *label_str); +static void fill_label_number(field_info *fi, gchar *label_str, gboolean is_signed); +static void fill_label_number64(field_info *fi, gchar *label_str, gboolean is_signed); + +static const char *hfinfo_number_vals_format(const header_field_info *hfinfo, char buf[32], guint32 value); +static const char *hfinfo_number_value_format(const header_field_info *hfinfo, char buf[32], guint32 value); +static const char *hfinfo_numeric_value_format(const header_field_info *hfinfo, char buf[32], guint32 value); + +static const char* hfinfo_uint64_format(const header_field_info *hfinfo); +static const char* hfinfo_int64_format(const header_field_info *hfinfo); + +static proto_item * +proto_tree_add_node(proto_tree *tree, field_info *fi); + +static void +get_hfi_and_length(header_field_info *hfinfo, tvbuff_t *tvb, const gint start, gint *length, + gint *item_length); + +static field_info * +new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, const gint item_length); + +static field_info * +alloc_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, gint *length); + +static proto_item * +proto_tree_add_pi(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + gint start, gint *length, field_info **pfi); + +static void +proto_tree_set_representation_value(proto_item *pi, const char *format, va_list ap); +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap); + +static void +proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb); +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length); +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length); +static void +proto_tree_set_time(field_info *fi, nstime_t *value_ptr); +static void +proto_tree_set_string(field_info *fi, const char* value); +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length, gint encoding); +static void +proto_tree_set_ax25(field_info *fi, const guint8* value); +static void +proto_tree_set_ax25_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_ether(field_info *fi, const guint8* value); +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value); +static void +proto_tree_set_ipv4(field_info *fi, guint32 value); +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr); +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length); +static void +proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr); +static void +proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding); +static void +proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length); +static void +proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length); +static void +proto_tree_set_boolean(field_info *fi, guint32 value); +static void +proto_tree_set_float(field_info *fi, float value); +static void +proto_tree_set_double(field_info *fi, double value); +static void +proto_tree_set_uint(field_info *fi, guint32 value); +static void +proto_tree_set_int(field_info *fi, gint32 value); +static void +proto_tree_set_uint64(field_info *fi, guint64 value); +static void +proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, guint length, const guint encoding); +static void +proto_tree_set_eui64(field_info *fi, const guint64 value); +static void +proto_tree_set_eui64_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding); +static gboolean +proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset, + const int len, const gint ett, const gint **fields, + const guint encoding, const int flags, + gboolean first); + +static int proto_register_field_init(header_field_info *hfinfo, const int parent); + +/* special-case header field used within proto.c */ +static header_field_info hfi_text_only = + { "Text item", "text", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }; +int hf_text_only = -1; + +/* Structure for information about a protocol */ +struct _protocol { + const char *name; /* long description */ + const char *short_name; /* short description */ + const char *filter_name; /* name of this protocol in filters */ + int proto_id; /* field ID for this protocol */ + GList *fields; /* fields for this protocol */ + GList *last_field; /* pointer to end of list of fields */ + gboolean is_enabled; /* TRUE if protocol is enabled */ + gboolean can_toggle; /* TRUE if is_enabled can be changed */ + gboolean is_private; /* TRUE is protocol is private */ +}; + +/* List of all protocols */ +static GList *protocols = NULL; + +#define INITIAL_NUM_PROTOCOL_HFINFO 1500 + +/* Contains information about a field when a dissector calls + * proto_tree_add_item. */ +#define FIELD_INFO_NEW(fi) fi = g_slice_new(field_info) +#define FIELD_INFO_FREE(fi) g_slice_free(field_info, fi) + +/* Contains the space for proto_nodes. */ +#define PROTO_NODE_NEW(node) \ + node = g_slice_new(proto_node); \ + node->first_child = NULL; \ + node->last_child = NULL; \ + node->next = NULL; + +#define PROTO_NODE_FREE(node) \ + g_slice_free(proto_node, node) + +/* String space for protocol and field items for the GUI */ +#define ITEM_LABEL_NEW(il) \ + il = g_slice_new(item_label_t); +#define ITEM_LABEL_FREE(il) \ + g_slice_free(item_label_t, il); + +#define PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo) \ + if((guint)hfindex >= gpa_hfinfo.len && getenv("WIRESHARK_ABORT_ON_DISSECTOR_BUG")) \ + g_error("Unregistered hf! index=%d", hfindex); \ + DISSECTOR_ASSERT_HINT((guint)hfindex < gpa_hfinfo.len, "Unregistered hf!"); \ + hfinfo = gpa_hfinfo.hfi[hfindex]; + +/* List which stores protocols and fields that have been registered */ +typedef struct _gpa_hfinfo_t { + guint32 len; + guint32 allocated_len; + header_field_info **hfi; +} gpa_hfinfo_t; +gpa_hfinfo_t gpa_hfinfo; + +/* Balanced tree of abbreviations and IDs */ +static GTree *gpa_name_tree = NULL; +static header_field_info *same_name_hfinfo; + +static void save_same_name_hfinfo(gpointer data) +{ + same_name_hfinfo = (header_field_info*)data; +} + +/* Points to the first element of an array of bits, indexed by + a subtree item type; that array element is TRUE if subtrees of + an item of that type are to be expanded. */ +static guint32 *tree_is_expanded; + +/* Number of elements in that array. */ +int num_tree_types; + +/* Name hashtables for fast detection of duplicate names */ +static GHashTable* proto_names = NULL; +static GHashTable* proto_short_names = NULL; +static GHashTable* proto_filter_names = NULL; + +static gint +proto_compare_name(gconstpointer p1_arg, gconstpointer p2_arg) +{ + const protocol_t *p1 = (const protocol_t *)p1_arg; + const protocol_t *p2 = (const protocol_t *)p2_arg; + + return g_ascii_strcasecmp(p1->short_name, p2->short_name); +} + + +/* initialize data structures and register protocols and fields */ +void +proto_init(void (register_all_protocols_func)(register_cb cb, gpointer client_data), + void (register_all_handoffs_func)(register_cb cb, gpointer client_data), + register_cb cb, + gpointer client_data) +{ + proto_cleanup(); + + proto_names = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); + proto_short_names = g_hash_table_new(wrs_str_hash, g_str_equal); + proto_filter_names = g_hash_table_new(wrs_str_hash, g_str_equal); + + gpa_hfinfo.len = 0; + gpa_hfinfo.allocated_len = 0; + gpa_hfinfo.hfi = NULL; + gpa_name_tree = g_tree_new_full(wrs_strcmp_with_data, NULL, NULL, save_same_name_hfinfo); + + /* Initialize the ftype subsystem */ + ftypes_initialize(); + + /* Register one special-case FT_TEXT_ONLY field for use when + converting wireshark to new-style proto_tree. These fields + are merely strings on the GUI tree; they are not filterable */ + hf_text_only = proto_register_field_init(&hfi_text_only, -1); + + /* Register the pseudo-protocols used for exceptions. */ + register_show_exception(); + + /* Have each built-in dissector register its protocols, fields, + dissector tables, and dissectors to be called through a + handle, and do whatever one-time initialization it needs to + do. */ + register_all_protocols_func(cb, client_data); +#ifdef HAVE_PYTHON + /* Now scan for python protocols */ + if (cb) + (*cb)(RA_PYTHON_REGISTER, NULL, client_data); + register_all_py_protocols_func(); +#endif + +#ifdef HAVE_PLUGINS + /* Now scan for plugins and load all the ones we find, calling + their register routines to do the stuff described above. */ + if (cb) + (*cb)(RA_PLUGIN_REGISTER, NULL, client_data); + init_plugins(); + register_all_plugin_registrations(); +#endif + + /* Now call the "handoff registration" routines of all built-in + dissectors; those routines register the dissector in other + dissectors' handoff tables, and fetch any dissector handles + they need. */ + register_all_handoffs_func(cb, client_data); + +#ifdef HAVE_PYTHON + /* Now do the same with python dissectors */ + if (cb) + (*cb)(RA_PYTHON_HANDOFF, NULL, client_data); + register_all_py_handoffs_func(); +#endif + +#ifdef HAVE_PLUGINS + /* Now do the same with plugins. */ + if (cb) + (*cb)(RA_PLUGIN_HANDOFF, NULL, client_data); + register_all_plugin_handoffs(); +#endif + + /* sort the protocols by protocol name */ + protocols = g_list_sort(protocols, proto_compare_name); + + /* We've assigned all the subtree type values; allocate the array + for them, and zero it out. */ + tree_is_expanded = g_new0(guint32, (num_tree_types/32)+1); +} + +void +proto_cleanup(void) +{ + /* Free the abbrev/ID GTree */ + if (gpa_name_tree) { + g_tree_destroy(gpa_name_tree); + gpa_name_tree = NULL; + } + + while (protocols) { + protocol_t *protocol = (protocol_t *)protocols->data; + header_field_info *hfinfo; + PROTO_REGISTRAR_GET_NTH(protocol->proto_id, hfinfo); + DISSECTOR_ASSERT(protocol->proto_id == hfinfo->id); + + g_slice_free(header_field_info, hfinfo); + g_list_free(protocol->fields); + protocols = g_list_remove(protocols, protocol); + g_free(protocol); + } + + if (proto_names) { + g_hash_table_destroy(proto_names); + proto_names = NULL; + } + + if (proto_short_names) { + g_hash_table_destroy(proto_short_names); + proto_short_names = NULL; + } + + if (proto_filter_names) { + g_hash_table_destroy(proto_filter_names); + proto_filter_names = NULL; + } + + if (gpa_hfinfo.allocated_len) { + gpa_hfinfo.len = 0; + gpa_hfinfo.allocated_len = 0; + g_free(gpa_hfinfo.hfi); + gpa_hfinfo.hfi = NULL; + } + g_free(tree_is_expanded); + tree_is_expanded = NULL; +} + +static gboolean +proto_tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, + gpointer data) +{ + proto_node *pnode = tree; + proto_node *child; + proto_node *current; + + if (func(pnode, data)) + return TRUE; + + child = pnode->first_child; + while (child != NULL) { + /* + * The routine we call might modify the child, e.g. by + * freeing it, so we get the child's successor before + * calling that routine. + */ + current = child; + child = current->next; + if (proto_tree_traverse_pre_order((proto_tree *)current, func, data)) + return TRUE; + } + + return FALSE; +} + +gboolean +proto_tree_traverse_post_order(proto_tree *tree, proto_tree_traverse_func func, + gpointer data) +{ + proto_node *pnode = tree; + proto_node *child; + proto_node *current; + + child = pnode->first_child; + while (child != NULL) { + /* + * The routine we call might modify the child, e.g. by + * freeing it, so we get the child's successor before + * calling that routine. + */ + current = child; + child = current->next; + if (proto_tree_traverse_post_order((proto_tree *)current, func, data)) + return TRUE; + } + if (func(pnode, data)) + return TRUE; + + return FALSE; +} + +void +proto_tree_children_foreach(proto_tree *tree, proto_tree_foreach_func func, + gpointer data) +{ + proto_node *node = tree; + proto_node *current; + + if (!node) + return; + + node = node->first_child; + while (node != NULL) { + current = node; + node = current->next; + func((proto_tree *)current, data); + } +} + +static void +free_GPtrArray_value(gpointer key, gpointer value, gpointer user_data _U_) +{ + GPtrArray *ptrs = (GPtrArray *)value; + gint hfid = (gint)(long)key; + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfid, hfinfo); + if (hfinfo->ref_type != HF_REF_TYPE_NONE) { + /* when a field is referenced by a filter this also + affects the refcount for the parent protocol so we need + to adjust the refcount for the parent as well + */ + if (hfinfo->parent != -1) { + header_field_info *parent_hfinfo; + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + parent_hfinfo->ref_type = HF_REF_TYPE_NONE; + } + hfinfo->ref_type = HF_REF_TYPE_NONE; + } + + g_ptr_array_free(ptrs, TRUE); +} + +static void +free_node_tree_data(tree_data_t *tree_data) +{ + if (tree_data->interesting_hfids) { + /* Free all the GPtrArray's in the interesting_hfids hash. */ + g_hash_table_foreach(tree_data->interesting_hfids, + free_GPtrArray_value, NULL); + + /* And then destroy the hash. */ + g_hash_table_destroy(tree_data->interesting_hfids); + } + if (tree_data->fi_tmp) + FIELD_INFO_FREE(tree_data->fi_tmp); + + /* And finally the tree_data_t itself. */ + g_free(tree_data); +} + +#define FREE_NODE_FIELD_INFO(finfo) \ + if (finfo->rep) { \ + ITEM_LABEL_FREE(finfo->rep); \ + } \ + FVALUE_CLEANUP(&finfo->value); \ + FIELD_INFO_FREE(finfo); + +static void +proto_tree_free_node(proto_node *node, gpointer data _U_) +{ + field_info *finfo = PNODE_FINFO(node); + + proto_tree_children_foreach(node, proto_tree_free_node, NULL); + + /* free the field_info data. */ + FREE_NODE_FIELD_INFO(finfo); + node->finfo = NULL; + + /* Free the proto_node. */ + PROTO_NODE_FREE(node); +} + +/* frees the resources that the dissection a proto_tree uses */ +void +proto_tree_free(proto_tree *tree) +{ + tree_data_t *tree_data = PTREE_DATA(tree); + + proto_tree_children_foreach(tree, proto_tree_free_node, NULL); + + /* free root node */ + PROTO_NODE_FREE(tree); + + /* free tree data */ + free_node_tree_data(tree_data); +} + +/* Is the parsing being done for a visible proto_tree or an invisible one? + * By setting this correctly, the proto_tree creation is sped up by not + * having to call g_vsnprintf and copy strings around. + */ +gboolean +proto_tree_set_visible(proto_tree *tree, gboolean visible) +{ + gboolean old_visible = PTREE_DATA(tree)->visible; + + PTREE_DATA(tree)->visible = visible; + + return old_visible; +} + +void +proto_tree_set_fake_protocols(proto_tree *tree, gboolean fake_protocols) +{ + PTREE_DATA(tree)->fake_protocols = fake_protocols; +} + +/* Assume dissector set only its protocol fields. + This function is called by dissectors and allows the speeding up of filtering + in wireshark; if this function returns FALSE it is safe to reset tree to NULL + and thus skip calling most of the expensive proto_tree_add_...() + functions. + If the tree is visible we implicitly assume the field is referenced. +*/ +gboolean +proto_field_is_referenced(proto_tree *tree, int proto_id) +{ + register header_field_info *hfinfo; + + + if (!tree) + return FALSE; + + if (PTREE_DATA(tree)->visible) + return TRUE; + + PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo); + if (hfinfo->ref_type != HF_REF_TYPE_NONE) + return TRUE; + + if (hfinfo->type == FT_PROTOCOL && !PTREE_DATA(tree)->fake_protocols) + return TRUE; + + return FALSE; +} + + +/* Finds a record in the hf_info_records array by id. */ +header_field_info * +proto_registrar_get_nth(guint hfindex) +{ + register header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + return hfinfo; +} + + +/* Prefix initialization + * this allows for a dissector to register a display filter name prefix + * so that it can delay the initialization of the hf array as long as + * possible. + */ + +/* compute a hash for the part before the dot of a display filter */ +static guint +prefix_hash (gconstpointer key) { + /* end the string at the dot and compute its hash */ + gchar* copy = ep_strdup((const gchar *)key); + gchar* c = copy; + + for (; *c; c++) { + if (*c == '.') { + *c = 0; + break; + } + } + + return g_str_hash(copy); +} + +/* are both strings equal up to the end or the dot? */ +static gboolean +prefix_equal (gconstpointer ap, gconstpointer bp) { + const gchar* a = (const gchar *)ap; + const gchar* b = (const gchar *)bp; + + do { + gchar ac = *a++; + gchar bc = *b++; + + if ( (ac == '.' || ac == '\0') && (bc == '.' || bc == '\0') ) return TRUE; + + if ( (ac == '.' || ac == '\0') && ! (bc == '.' || bc == '\0') ) return FALSE; + if ( (bc == '.' || bc == '\0') && ! (ac == '.' || ac == '\0') ) return FALSE; + + if (ac != bc) return FALSE; + } while (1); + + return FALSE; +} + + +/* indexed by prefix, contains initializers */ +static GHashTable* prefixes = NULL; + + +/* Register a new prefix for "delayed" initialization of field arrays */ +void +proto_register_prefix(const char *prefix, prefix_initializer_t pi ) { + if (! prefixes ) { + prefixes = g_hash_table_new(prefix_hash, prefix_equal); + } + + g_hash_table_insert(prefixes, (gpointer)prefix, pi); +} + +/* helper to call all prefix initializers */ +static gboolean +initialize_prefix(gpointer k, gpointer v, gpointer u _U_) { + ((prefix_initializer_t)v)((const char *)k); + return TRUE; +} + +/** Initialize every remaining uninitialized prefix. */ +void +proto_initialize_all_prefixes(void) { + g_hash_table_foreach_remove(prefixes, initialize_prefix, NULL); +} + +/* Finds a record in the hf_info_records array by name. + * If it fails to find it in the already registered fields, + * it tries to find and call an initializer in the prefixes + * table and if so it looks again. + */ +header_field_info * +proto_registrar_get_byname(const char *field_name) +{ + header_field_info *hfinfo; + prefix_initializer_t pi; + + if (!field_name) + return NULL; + + hfinfo = (header_field_info *)g_tree_lookup(gpa_name_tree, field_name); + + if (hfinfo) + return hfinfo; + + if (!prefixes) + return NULL; + + if ((pi = (prefix_initializer_t)g_hash_table_lookup(prefixes, field_name) ) != NULL) { + pi(field_name); + g_hash_table_remove(prefixes, field_name); + } else { + return NULL; + } + + return (header_field_info *)g_tree_lookup(gpa_name_tree, field_name); +} + +int +proto_registrar_get_id_byname(const char *field_name) +{ + header_field_info *hfinfo; + + hfinfo = proto_registrar_get_byname(field_name); + + if (!hfinfo) + return -1; + + return hfinfo->id; +} + + +static void +ptvcursor_new_subtree_levels(ptvcursor_t *ptvc) +{ + subtree_lvl *pushed_tree; + + DISSECTOR_ASSERT(ptvc->pushed_tree_max <= SUBTREE_MAX_LEVELS-SUBTREE_ONCE_ALLOCATION_NUMBER); + ptvc->pushed_tree_max += SUBTREE_ONCE_ALLOCATION_NUMBER; + + pushed_tree = (subtree_lvl *)ep_alloc(sizeof(subtree_lvl) * ptvc->pushed_tree_max); + DISSECTOR_ASSERT(pushed_tree != NULL); + if (ptvc->pushed_tree) + memcpy(pushed_tree, ptvc->pushed_tree, ptvc->pushed_tree_max - SUBTREE_ONCE_ALLOCATION_NUMBER); + ptvc->pushed_tree = pushed_tree; +} + +static void +ptvcursor_free_subtree_levels(ptvcursor_t *ptvc) +{ + ptvc->pushed_tree = NULL; + ptvc->pushed_tree_max = 0; + DISSECTOR_ASSERT(ptvc->pushed_tree_index == 0); + ptvc->pushed_tree_index = 0; +} + +/* Allocates an initializes a ptvcursor_t with 3 variables: + * proto_tree, tvbuff, and offset. */ +ptvcursor_t * +ptvcursor_new(proto_tree *tree, tvbuff_t *tvb, gint offset) +{ + ptvcursor_t *ptvc; + + ptvc = (ptvcursor_t *)ep_alloc(sizeof(ptvcursor_t)); + ptvc->tree = tree; + ptvc->tvb = tvb; + ptvc->offset = offset; + ptvc->pushed_tree = NULL; + ptvc->pushed_tree_max = 0; + ptvc->pushed_tree_index = 0; + return ptvc; +} + + +/* Frees memory for ptvcursor_t, but nothing deeper than that. */ +void +ptvcursor_free(ptvcursor_t *ptvc) +{ + ptvcursor_free_subtree_levels(ptvc); + /*g_free(ptvc);*/ +} + +/* Returns tvbuff. */ +tvbuff_t * +ptvcursor_tvbuff(ptvcursor_t *ptvc) +{ + return ptvc->tvb; +} + +/* Returns current offset. */ +gint +ptvcursor_current_offset(ptvcursor_t *ptvc) +{ + return ptvc->offset; +} + +proto_tree * +ptvcursor_tree(ptvcursor_t *ptvc) +{ + if (!ptvc) + return NULL; + + return ptvc->tree; +} + +void +ptvcursor_set_tree(ptvcursor_t *ptvc, proto_tree *tree) +{ + ptvc->tree = tree; +} + +/* creates a subtree, sets it as the working tree and pushes the old working tree */ +proto_tree * +ptvcursor_push_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) +{ + subtree_lvl *subtree; + if (ptvc->pushed_tree_index >= ptvc->pushed_tree_max) + ptvcursor_new_subtree_levels(ptvc); + + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index; + subtree->tree = ptvc->tree; + subtree->it= NULL; + ptvc->pushed_tree_index++; + return ptvcursor_set_subtree(ptvc, it, ett_subtree); +} + +/* pops a subtree */ +void +ptvcursor_pop_subtree(ptvcursor_t *ptvc) +{ + subtree_lvl *subtree; + + if (ptvc->pushed_tree_index <= 0) + return; + + ptvc->pushed_tree_index--; + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index; + if (subtree->it != NULL) + proto_item_set_len(subtree->it, ptvcursor_current_offset(ptvc) - subtree->cursor_offset); + + ptvc->tree = subtree->tree; +} + +/* saves the current tvb offset and the item in the current subtree level */ +static void +ptvcursor_subtree_set_item(ptvcursor_t *ptvc, proto_item *it) +{ + subtree_lvl *subtree; + + DISSECTOR_ASSERT(ptvc->pushed_tree_index > 0); + + subtree = ptvc->pushed_tree + ptvc->pushed_tree_index - 1; + subtree->it = it; + subtree->cursor_offset = ptvcursor_current_offset(ptvc); +} + +/* Creates a subtree and adds it to the cursor as the working tree but does not + * save the old working tree */ +proto_tree * +ptvcursor_set_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) +{ + ptvc->tree = proto_item_add_subtree(it, ett_subtree); + return ptvc->tree; +} + +static proto_tree * +ptvcursor_add_subtree_item(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree, gint length) +{ + ptvcursor_push_subtree(ptvc, it, ett_subtree); + if (length == SUBTREE_UNDEFINED_LENGTH) + ptvcursor_subtree_set_item(ptvc, it); + return ptvcursor_tree(ptvc); +} + +/* Add an item to the tree and create a subtree + * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. + * In this case, when the subtree will be closed, the parent item length will + * be equal to the advancement of the cursor since the creation of the subtree. + */ +proto_tree * +ptvcursor_add_with_subtree(ptvcursor_t *ptvc, int hfindex, gint length, + const guint encoding, gint ett_subtree) +{ + proto_item *it; + + it = ptvcursor_add_no_advance(ptvc, hfindex, length, encoding); + return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length); +} + +static proto_item * +proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length); + +/* Add a text node to the tree and create a subtree + * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. + * In this case, when the subtree will be closed, the item length will be equal + * to the advancement of the cursor since the creation of the subtree. + */ +proto_tree * +ptvcursor_add_text_with_subtree(ptvcursor_t *ptvc, gint length, + gint ett_subtree, const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + proto_tree *tree; + + tree = ptvcursor_tree(ptvc); + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, ptvcursor_tvbuff(ptvc), + ptvcursor_current_offset(ptvc), length); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return ptvcursor_add_subtree_item(ptvc, pi, ett_subtree, length); +} + +/* Add a text-only node, leaving it to our caller to fill the text in */ +static proto_item * +proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length) +{ + proto_item *pi; + field_info *new_fi; + + if (tree == NULL) + return NULL; + + pi = proto_tree_add_pi(tree, &hfi_text_only, tvb, start, &length, &new_fi); + + return pi; +} + +/* Add a text-only node to the proto_tree */ +proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, tvb, start, length); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Add a text-only node to the proto_tree (va_list version) */ +proto_item * +proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start, + gint length, const char *format, va_list ap) +{ + proto_item *pi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_text_only, hfinfo); + + pi = proto_tree_add_text_node(tree, tvb, start, length); + + TRY_TO_FAKE_THIS_REPR(pi); + + proto_tree_set_representation(pi, format, ap); + + return pi; +} + +/* Add a text-only node for debugging purposes. The caller doesn't need + * to worry about tvbuff, start, or length. Debug message gets sent to + * STDOUT, too */ +proto_item * +proto_tree_add_debug_text(proto_tree *tree, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_text_node(tree, NULL, 0, 0); + + if (pi) { + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + printf("\n"); + + return pi; +} + +/* We could probably get away with changing is_error to a minimum length value. */ +static void +report_type_length_mismatch(proto_tree *tree, const gchar *descr, int length, gboolean is_error) { + tree_data_t *tree_data = PTREE_DATA(tree); + field_info *fi_save = tree_data->fi_tmp; + + /* Keep the current item from getting freed by proto_tree_new_item. */ + tree_data->fi_tmp = NULL; + + expert_add_info_format(NULL, tree, PI_MALFORMED, is_error ? PI_ERROR : PI_WARN, "Trying to fetch %s with length %d", descr, length); + + tree_data->fi_tmp = fi_save; + + if (is_error) { + THROW(ReportedBoundsError); + } +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static guint32 +get_uint_value(proto_tree *tree, tvbuff_t *tvb, gint offset, gint length, const guint encoding) +{ + guint32 value; + gboolean length_error; + + switch (length) { + + case 1: + value = tvb_get_guint8(tvb, offset); + break; + + case 2: + value = encoding ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset); + break; + + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + break; + + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + if (length < 1) { + length_error = TRUE; + value = 0; + } else { + length_error = FALSE; + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + } + report_type_length_mismatch(tree, "an unsigned integer", length, length_error); + break; + } + return value; +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static gint32 +get_int_value(proto_tree *tree, tvbuff_t *tvb, gint offset, gint length, const guint encoding) +{ + gint32 value; + gboolean length_error; + + switch (length) { + + case 1: + value = (gint8)tvb_get_guint8(tvb, offset); + break; + + case 2: + value = (gint16) (encoding ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset)); + break; + + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + if (value & 0x00800000) { + /* Sign bit is set; sign-extend it. */ + value |= 0xFF000000; + } + break; + + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + if (length < 1) { + length_error = TRUE; + value = 0; + } else { + length_error = FALSE; + value = encoding ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + } + report_type_length_mismatch(tree, "a signed integer", length, length_error); + break; + } + return value; +} + +static void +tree_data_add_maybe_interesting_field(tree_data_t *tree_data, field_info *fi) +{ + const header_field_info *hfinfo = fi->hfinfo; + + if (hfinfo->ref_type == HF_REF_TYPE_DIRECT) { + GPtrArray *ptrs = NULL; + + if (tree_data->interesting_hfids == NULL) { + /* Initialize the hash because we now know that it is needed */ + tree_data->interesting_hfids = + g_hash_table_new(g_direct_hash, NULL /* g_direct_equal */); + } else + ptrs = (GPtrArray *)g_hash_table_lookup(tree_data->interesting_hfids, + GINT_TO_POINTER(hfinfo->id)); + if (!ptrs) { + /* First element triggers the creation of pointer array */ + ptrs = g_ptr_array_new(); + g_hash_table_insert(tree_data->interesting_hfids, + GINT_TO_POINTER(hfinfo->id), ptrs); + } + + g_ptr_array_add(ptrs, fi); + } +} + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +static proto_item * +proto_tree_new_item(field_info *new_fi, proto_tree *tree, + tvbuff_t *tvb, gint start, gint length, + guint encoding) +{ + tree_data_t *tree_data = PTREE_DATA(tree); + proto_item *pi; + guint32 value, n; + float floatval; + double doubleval; + const char *string; + nstime_t time_stamp; + guint32 tmpsecs; + gboolean length_error; + + /* there is a possibility here that we might raise an exception + * and thus would lose track of the field_info. + * store it in a temp so that if we come here again we can reclaim + * the field_info without leaking memory. + */ + if (tree_data->fi_tmp) { + /* oops, last one we got must have been lost due + * to an exception. + * good thing we saved it, now we can reverse the + * memory leak and reclaim it. + */ + FIELD_INFO_FREE(tree_data->fi_tmp); + } + /* we might throw an exception, keep track of this one + * across the "dangerous" section below. + */ + tree_data->fi_tmp = new_fi; + + switch (new_fi->hfinfo->type) { + case FT_NONE: + /* no value to set for FT_NONE */ + break; + + case FT_PROTOCOL: + proto_tree_set_protocol_tvb(new_fi, tvb); + break; + + case FT_BYTES: + proto_tree_set_bytes_tvb(new_fi, tvb, start, length); + break; + + case FT_UINT_BYTES: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + n = get_uint_value(tree, tvb, start, length, encoding); + proto_tree_set_bytes_tvb(new_fi, tvb, start + length, n); + + /* Instead of calling proto_item_set_len(), since we don't yet + * have a proto_item, we set the field_info's length ourselves. */ + new_fi->length = n + length; + break; + + case FT_BOOLEAN: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_boolean(new_fi, + get_uint_value(tree, tvb, start, length, encoding)); + break; + + /* XXX - make these just FT_UINT? */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_uint(new_fi, + get_uint_value(tree, tvb, start, length, encoding)); + break; + + case FT_INT64: + case FT_UINT64: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length < 1 || length > 8) { + length_error = length < 1 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a 64-bit integer", length, length_error); + } + proto_tree_set_uint64_tvb(new_fi, tvb, start, length, encoding); + break; + + /* XXX - make these just FT_INT? */ + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + proto_tree_set_int(new_fi, + get_int_value(tree, tvb, start, length, encoding)); + break; + + case FT_IPv4: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_IPv4_LEN) { + length_error = length < FT_IPv4_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPv4 address", length, length_error); + } + value = tvb_get_ipv4(tvb, start); + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + */ + proto_tree_set_ipv4(new_fi, encoding ? GUINT32_SWAP_LE_BE(value) : value); + break; + + case FT_IPXNET: + if (length != FT_IPXNET_LEN) { + length_error = length < FT_IPXNET_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPXNET address", length, length_error); + } + proto_tree_set_ipxnet(new_fi, + get_uint_value(tree, tvb, start, FT_IPXNET_LEN, FALSE)); + break; + + case FT_IPv6: + if (length != FT_IPv6_LEN) { + length_error = length < FT_IPv6_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an IPv6 address", length, length_error); + } + proto_tree_set_ipv6_tvb(new_fi, tvb, start, length); + break; + + case FT_AX25: + if (length != 7) { + length_error = length < 7 ? TRUE : FALSE; + expert_add_info_format(NULL, tree, PI_MALFORMED, PI_ERROR, "Trying to fetch an AX.25 address with length %d", length); + THROW(ReportedBoundsError); + } + proto_tree_set_ax25_tvb(new_fi, tvb, start); + break; + + case FT_ETHER: + if (length != FT_ETHER_LEN) { + length_error = length < FT_ETHER_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an Ethernet", length, length_error); + } + proto_tree_set_ether_tvb(new_fi, tvb, start); + break; + + case FT_EUI64: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_EUI64_LEN) { + length_error = length < FT_EUI64_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "an EUI-64 address", length, length_error); + } + proto_tree_set_eui64_tvb(new_fi, tvb, start, encoding); + break; + case FT_GUID: + /* + * Map all non-zero values to little-endian for + * backwards compatibility. + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != FT_GUID_LEN) { + length_error = length < FT_GUID_LEN ? TRUE : FALSE; + report_type_length_mismatch(tree, "a GUID", length, length_error); + } + proto_tree_set_guid_tvb(new_fi, tvb, start, encoding); + break; + + case FT_OID: + proto_tree_set_oid_tvb(new_fi, tvb, start, length); + break; + + case FT_FLOAT: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + * + * At some point in the future, we might + * support non-IEEE-binary floating-point + * formats in the encoding as well + * (IEEE decimal, System/3x0, VAX). + */ + if (encoding) + encoding = ENC_LITTLE_ENDIAN; + if (length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a single-precision floating point number", length, length_error); + } + if (encoding) + floatval = tvb_get_letohieee_float(tvb, start); + else + floatval = tvb_get_ntohieee_float(tvb, start); + proto_tree_set_float(new_fi, floatval); + break; + + case FT_DOUBLE: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any + * non-zero value of "encoding" as meaning + * "little-endian". + * + * At some point in the future, we might + * support non-IEEE-binary floating-point + * formats in the encoding as well + * (IEEE decimal, System/3x0, VAX). + */ + if (encoding == TRUE) + encoding = ENC_LITTLE_ENDIAN; + if (length != 8) { + length_error = length < 8 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a double-precision floating point number", length, length_error); + } + if (encoding) + doubleval = tvb_get_letohieee_double(tvb, start); + else + doubleval = tvb_get_ntohieee_double(tvb, start); + proto_tree_set_double(new_fi, doubleval); + break; + + case FT_STRING: + proto_tree_set_string_tvb(new_fi, tvb, start, length, + encoding); + break; + + case FT_STRINGZ: + if (length < -1 ) { + report_type_length_mismatch(tree, "a string", length, TRUE); + } + /* Instead of calling proto_item_set_len(), + * since we don't yet have a proto_item, we + * set the field_info's length ourselves. + * + * XXX - our caller can't use that length to + * advance an offset unless they arrange that + * there always be a protocol tree into which + * we're putting this item. + */ + if (length == -1) { + /* This can throw an exception */ + string = tvb_get_stringz_enc(tvb, start, &length, encoding); + } else if (length == 0) { + string = "[Empty]"; + } else { + /* In this case, length signifies + * the length of the string. + * + * This could either be a null-padded + * string, which doesn't necessarily + * have a '\0' at the end, or a + * null-terminated string, with a + * trailing '\0'. (Yes, there are + * cases where you have a string + * that's both counted and null- + * terminated.) + * + * In the first case, we must + * allocate a buffer of length + * "length+1", to make room for + * a trailing '\0'. + * + * In the second case, we don't + * assume that there is a trailing + * '\0' there, as the packet might + * be malformed. (XXX - should we + * throw an exception if there's no + * trailing '\0'?) Therefore, we + * allocate a buffer of length + * "length+1", and put in a trailing + * '\0', just to be safe. + * + * (XXX - this would change if + * we made string values counted + * rather than null-terminated.) + */ + string = tvb_get_ephemeral_string_enc(tvb, start, length, encoding); + } + new_fi->length = length; + proto_tree_set_string(new_fi, string); + break; + + case FT_UINT_STRING: + /* + * NOTE: to support code written when + * proto_tree_add_item() took a gboolean as its + * last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", if the + * encoding value is TRUE, treat that as + * ASCII with a little-endian length. + * + * This won't work for code that passes + * arbitrary non-zero values; that code + * will need to be fixed. + */ + if (encoding == TRUE) + encoding = ENC_ASCII|ENC_LITTLE_ENDIAN; + n = get_uint_value(tree, tvb, start, length, encoding & ~ENC_CHARENCODING_MASK); + proto_tree_set_string_tvb(new_fi, tvb, start + length, n, + encoding); + + /* Instead of calling proto_item_set_len(), since we + * don't yet have a proto_item, we set the + * field_info's length ourselves. + * + * XXX - our caller can't use that length to + * advance an offset unless they arrange that + * there always be a protocol tree into which + * we're putting this item. + */ + new_fi->length = n + length; + break; + + case FT_ABSOLUTE_TIME: + /* + * Absolute times can be in any of a number of + * formats, and they can be big-endian or + * little-endian. + * + * Historically FT_TIMEs were only timespecs; + * the only question was whether they were stored + * in big- or little-endian format. + * + * For backwards compatibility, we interpret an + * encoding of 1 as meaning "little-endian timespec", + * so that passing TRUE is interpreted as that. + */ + if (encoding == TRUE) + encoding = ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN; + + if (length != 8 && length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "an absolute time value", length, length_error); + } + + switch (encoding) { + + case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both big-endian. + */ + time_stamp.secs = tvb_get_ntohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_ntohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both little-endian. + */ + time_stamp.secs = tvb_get_letohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_letohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_NTP|ENC_BIG_ENDIAN: + /* + * NTP time stamp, big-endian. + */ + +/* XXX - where should this go? */ +#define NTP_BASETIME 2208988800ul + + /* We need a temporary variable here so the unsigned math + * works correctly (for years > 2036 according to RFC 2030 + * chapter 3). + */ + tmpsecs = tvb_get_ntohl(tvb, start); + if (tmpsecs) + time_stamp.secs = tmpsecs - (guint32)NTP_BASETIME; + else + time_stamp.secs = tmpsecs; /* 0 */ + + if (length == 8) { + /* + * We're using nanoseconds here (and we will + * display nanoseconds), but NTP's timestamps + * have a precision in microseconds or greater. + * Round to 1 microsecond. + */ + time_stamp.nsecs = (int)(1000000*(tvb_get_ntohl(tvb, start+4)/4294967296.0)); + time_stamp.nsecs *= 1000; + } else { + time_stamp.nsecs = 0; + } + break; + + case ENC_TIME_NTP|ENC_LITTLE_ENDIAN: + /* + * NTP time stamp, big-endian. + */ + tmpsecs = tvb_get_letohl(tvb, start); + if (tmpsecs) + time_stamp.secs = tmpsecs - (guint32)NTP_BASETIME; + else + time_stamp.secs = tmpsecs; /* 0 */ + + if (length == 8) { + /* + * We're using nanoseconds here (and we will + * display nanoseconds), but NTP's timestamps + * have a precision in microseconds or greater. + * Round to 1 microsecond. + */ + time_stamp.nsecs = (int)(1000000*(tvb_get_letohl(tvb, start+4)/4294967296.0)); + time_stamp.nsecs *= 1000; + } else { + time_stamp.nsecs = 0; + } + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + time_stamp.secs = 0; + time_stamp.nsecs = 0; + break; + } + proto_tree_set_time(new_fi, &time_stamp); + break; + + case FT_RELATIVE_TIME: + /* + * Relative times can be in any of a number of + * formats, and they can be big-endian or + * little-endian. + * + * Historically FT_TIMEs were only timespecs; + * the only question was whether they were stored + * in big- or little-endian format. + * + * For backwards compatibility, we interpret an + * encoding of 1 as meaning "little-endian timespec", + * so that passing TRUE is interpreted as that. + */ + if (encoding == TRUE) + encoding = ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN; + switch (encoding) { + + if (length != 8 && length != 4) { + length_error = length < 4 ? TRUE : FALSE; + report_type_length_mismatch(tree, "a relative time value", length, length_error); + } + + case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both big-endian. + */ + time_stamp.secs = tvb_get_ntohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_ntohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + + case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN: + /* + * 4-byte UNIX epoch, possibly followed by + * 4-byte fractional time in nanoseconds, + * both little-endian. + */ + time_stamp.secs = tvb_get_letohl(tvb, start); + if (length == 8) + time_stamp.nsecs = tvb_get_letohl(tvb, start+4); + else + time_stamp.nsecs = 0; + break; + } + proto_tree_set_time(new_fi, &time_stamp); + break; + + default: + g_error("new_fi->hfinfo->type %d (%s) not handled\n", + new_fi->hfinfo->type, + ftype_name(new_fi->hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + FI_SET_FLAG(new_fi, (encoding & ENC_LITTLE_ENDIAN) ? FI_LITTLE_ENDIAN : FI_BIG_ENDIAN); + + /* Don't add new node to proto_tree until now so that any exceptions + * raised by a tvbuff access method doesn't leave junk in the proto_tree. */ + /* XXX. wouldn't be better to add this item to tree, with some special flag (FI_EXCEPTION?) + * to know which item caused exception? */ + pi = proto_tree_add_node(tree, new_fi); + + /* we did not raise an exception so we dont have to remember this + * field_info struct any more. + */ + tree_data->fi_tmp = NULL; + + return pi; +} + +/* Gets data from tvbuff, adds it to proto_tree, increments offset, + and returns proto_item* */ +proto_item * +ptvcursor_add(ptvcursor_t *ptvc, int hfindex, gint length, + const guint encoding) +{ + field_info *new_fi; + header_field_info *hfinfo; + gint item_length; + guint32 n; + int offset; + + /* We can't fake it just yet. We have to advance the cursor + TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo); */ + + offset = ptvc->offset; + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + get_hfi_and_length(hfinfo, ptvc->tvb, offset, &length, &item_length); + ptvc->offset += length; + if (hfinfo->type == FT_UINT_BYTES || hfinfo->type == FT_UINT_STRING) { + /* + * The length of the rest of the item is in the first N + * bytes of the item. + */ + n = get_uint_value(ptvc->tree, ptvc->tvb, offset, length, encoding); + ptvc->offset += n; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(ptvc->tree, hfindex, hfinfo); + + new_fi = new_field_info(ptvc->tree, hfinfo, ptvc->tvb, offset, item_length); + + return proto_tree_new_item(new_fi, ptvc->tree, ptvc->tvb, + offset, length, encoding); +} + +/* + * Validates that field length bytes are available starting from + * start (pos/neg). Throws an exception if they aren't. + */ +static void +test_length(header_field_info *hfinfo, proto_tree *tree, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian) +{ + gint size = length; + + if (!tvb) + return; + + if (hfinfo->type == FT_UINT_BYTES || hfinfo->type == FT_UINT_STRING) { + guint32 n; + + n = get_uint_value(tree, tvb, start, length, little_endian); + if (n > size + n) { + /* If n > size + n then we have an integer overflow, so + * set size to -1, which will force the + * tvb_ensure_bytes_exist call below to throw a + * ReportedBoundsError + */ + size = -1; + } + else { + size += n; + } + } + tvb_ensure_bytes_exist(tvb, start, size); +} + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +proto_item * +proto_tree_add_item(proto_tree *tree, const int hfindex, tvbuff_t *tvb, + const gint start, gint length, const guint encoding) +{ + field_info *new_fi; + header_field_info *hfinfo; + gint item_length; + + PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); + get_hfi_and_length(hfinfo, tvb, start, &length, &item_length); + test_length(hfinfo, tree, tvb, start, item_length, encoding); + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + new_fi = new_field_info(tree, hfinfo, tvb, start, item_length); + + if (new_fi == NULL) + return NULL; + + return proto_tree_new_item(new_fi, tree, tvb, start, length, encoding); +} + +/* Add a FT_NONE to a proto_tree */ +proto_item * +proto_tree_add_none_format(proto_tree *tree, const int hfindex, tvbuff_t *tvb, + const gint start, gint length, const char *format, + ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + field_info *new_fi; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_NONE); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + /* no value to set for FT_NONE */ + return pi; +} + +/* Gets data from tvbuff, adds it to proto_tree, *DOES NOT* increment + * offset, and returns proto_item* */ +proto_item * +ptvcursor_add_no_advance(ptvcursor_t* ptvc, int hf, gint length, + const guint encoding) +{ + proto_item *item; + + item = proto_tree_add_item(ptvc->tree, hf, ptvc->tvb, ptvc->offset, + length, encoding); + + return item; +} + +/* Advance the ptvcursor's offset within its tvbuff without + * adding anything to the proto_tree. */ +void +ptvcursor_advance(ptvcursor_t* ptvc, gint length) +{ + ptvc->offset += length; +} + + +static void +proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb) +{ + fvalue_set(&fi->value, tvb, TRUE); +} + +/* Add a FT_PROTOCOL to a proto_tree */ +proto_item * +proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char *format, ...) +{ + proto_item *pi; + va_list ap; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_PROTOCOL); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + + proto_tree_set_protocol_tvb(new_fi, (start == 0 ? tvb : tvb_new_subset(tvb, start, length, length))); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + + +/* Add a FT_BYTES to a proto_tree */ +proto_item * +proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8 *start_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_BYTES); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_bytes(new_fi, start_ptr, length); + + return pi; +} + +proto_item * +proto_tree_add_bytes_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8 *start_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + if (start_ptr) + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + start_ptr); + else + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + tvb_get_ptr(tvb, start, length)); + + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + + return pi; +} + +proto_item * +proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8 *start_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + if (start_ptr) + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + start_ptr); + else + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, + tvb_get_ptr(tvb, start, length)); + + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length) +{ + GByteArray *bytes; + + bytes = g_byte_array_new(); + if (length > 0) { + g_byte_array_append(bytes, start_ptr, length); + } + fvalue_set(&fi->value, bytes, TRUE); +} + + +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length) +{ + proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length); +} + +/* Add a FT_*TIME to a proto_tree */ +proto_item * +proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, nstime_t *value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_ABSOLUTE_TIME || + hfinfo->type == FT_RELATIVE_TIME); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_time(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_time_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, nstime_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, nstime_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_*TIME value */ +static void +proto_tree_set_time(field_info *fi, nstime_t *value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + + fvalue_set(&fi->value, value_ptr, FALSE); +} + +/* Add a FT_IPXNET to a proto_tree */ +proto_item * +proto_tree_add_ipxnet(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPXNET); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipxnet(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPXNET value */ +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value) +{ + fvalue_set_uinteger(&fi->value, value); +} + +/* Add a FT_IPv4 to a proto_tree */ +proto_item * +proto_tree_add_ipv4(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPv4); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipv4(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipv4_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPv4 value */ +static void +proto_tree_set_ipv4(field_info *fi, guint32 value) +{ + fvalue_set_uinteger(&fi->value, value); +} + +/* Add a FT_IPv6 to a proto_tree */ +proto_item * +proto_tree_add_ipv6(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_IPv6); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ipv6(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_ipv6_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_IPv6 value */ +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + fvalue_set(&fi->value, (gpointer) value_ptr, FALSE); +} + +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) +{ + proto_tree_set_ipv6(fi, tvb_get_ptr(tvb, start, length)); +} + +/* Add a FT_GUID to a proto_tree */ +proto_item * +proto_tree_add_guid(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const e_guid_t *value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_GUID); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_guid(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_guid_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const e_guid_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_guid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_guid_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const e_guid_t *value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_guid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_GUID value */ +static void +proto_tree_set_guid(field_info *fi, const e_guid_t *value_ptr) +{ + DISSECTOR_ASSERT(value_ptr != NULL); + fvalue_set(&fi->value, (gpointer) value_ptr, FALSE); +} + +static void +proto_tree_set_guid_tvb(field_info *fi, tvbuff_t *tvb, gint start, + const guint encoding) +{ + e_guid_t guid; + + tvb_get_guid(tvb, start, &guid, encoding); + proto_tree_set_guid(fi, &guid); +} + +/* Add a FT_OID to a proto_tree */ +proto_item * +proto_tree_add_oid(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_OID); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_oid(new_fi, value_ptr, length); + + return pi; +} + +proto_item * +proto_tree_add_oid_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, + const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_oid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_oid_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value_ptr, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_oid(tree, hfindex, tvb, start, length, value_ptr); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_OID value */ +static void +proto_tree_set_oid(field_info *fi, const guint8* value_ptr, gint length) +{ + GByteArray *bytes; + + DISSECTOR_ASSERT(value_ptr != NULL); + + bytes = g_byte_array_new(); + if (length > 0) { + g_byte_array_append(bytes, value_ptr, length); + } + fvalue_set(&fi->value, bytes, TRUE); +} + +static void +proto_tree_set_oid_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) +{ + proto_tree_set_oid(fi, tvb_get_ptr(tvb, start, length), length); +} + +static void +proto_tree_set_uint64(field_info *fi, guint64 value) +{ + fvalue_set_integer64(&fi->value, value); +} + +/* + * NOTE: to support code written when proto_tree_add_item() took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static void +proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, + guint length, const guint encoding) +{ + guint64 value = 0; + guint8* b = (guint8 *)ep_tvb_memdup(tvb, start, length); + + if (encoding) { + b += length; + switch (length) { + default: DISSECTOR_ASSERT_NOT_REACHED(); + case 8: value <<= 8; value += *--b; + case 7: value <<= 8; value += *--b; + case 6: value <<= 8; value += *--b; + case 5: value <<= 8; value += *--b; + case 4: value <<= 8; value += *--b; + case 3: value <<= 8; value += *--b; + case 2: value <<= 8; value += *--b; + case 1: value <<= 8; value += *--b; + break; + } + } else { + switch (length) { + default: DISSECTOR_ASSERT_NOT_REACHED(); + case 8: value <<= 8; value += *b++; + case 7: value <<= 8; value += *b++; + case 6: value <<= 8; value += *b++; + case 5: value <<= 8; value += *b++; + case 4: value <<= 8; value += *b++; + case 3: value <<= 8; value += *b++; + case 2: value <<= 8; value += *b++; + case 1: value <<= 8; value += *b++; + break; + } + } + + proto_tree_set_uint64(fi, value); +} + +/* Add a FT_STRING or FT_STRINGZ to a proto_tree. Creates own copy of string, + * and frees it when the proto_tree is destroyed. */ +proto_item * +proto_tree_add_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + DISSECTOR_ASSERT(length >= 0); + proto_tree_set_string(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_unicode_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + DISSECTOR_ASSERT(g_utf8_validate(value, -1, NULL)); + return proto_tree_add_string_format_value(tree, hfindex, tvb, start, length, value, "%s", value); +} + +proto_item * +proto_tree_add_string_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char* value, + const char *format, + ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const char* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Appends string data to a FT_STRING or FT_STRINGZ, allowing progressive + * field info update instead of only updating the representation as does + * proto_item_append_text() + */ +/* NOTE: this function will break with the TRY_TO_FAKE_THIS_ITEM() + * speed optimization. + * Currently only WSP use this function so it is not that bad but try to + * avoid using this one if possible. + * IF you must use this function you MUST also disable the + * TRY_TO_FAKE_THIS_ITEM() optimization for your dissector/function + * using proto_item_append_string(). + * Do that by faking that the tree is visible by calling + * proto_tree_set_visible(tree, TRUE) (see packet-wsp.c) + * BEFORE you create the item you are later going to use + * proto_item_append_string() on. + */ +void +proto_item_append_string(proto_item *pi, const char *str) +{ + field_info *fi; + header_field_info *hfinfo; + gchar *old_str, *new_str; + + if (!pi) + return; + if (!*str) + return; + + fi = PITEM_FINFO(pi); + DISSECTOR_ASSERT_HINT(fi, "proto_tree_set_visible(tree, TRUE) should have been called previously"); + + hfinfo = fi->hfinfo; + if (hfinfo->type == FT_PROTOCOL) { + /* TRY_TO_FAKE_THIS_ITEM() speed optimization: silently skip */ + return; + } + DISSECTOR_ASSERT(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); + old_str = (guint8 *)fvalue_get(&fi->value); + new_str = ep_strdup_printf("%s%s", old_str, str); + fvalue_set(&fi->value, new_str, FALSE); +} + +/* Set the FT_STRING value */ +static void +proto_tree_set_string(field_info *fi, const char* value) +{ + if (value) { + fvalue_set(&fi->value, (gpointer) value, FALSE); + } else { + fvalue_set(&fi->value, (gpointer) "[ Null ]", FALSE); + } +} + +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length, gint encoding) +{ + gchar *string; + + if (length == -1) { + length = tvb_ensure_length_remaining(tvb, start); + } + + string = tvb_get_ephemeral_string_enc(tvb, start, length, encoding); + proto_tree_set_string(fi, string); +} + + +/* Add a FT_AX25 to a proto_tree */ +proto_item * +proto_tree_add_ax25(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_AX25); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ax25(new_fi, value); + + return pi; +} + +/* Set the FT_AX25 value */ +static void +proto_tree_set_ax25(field_info *fi, const guint8* value) +{ + fvalue_set(&fi->value, (gpointer) value, FALSE); +} + +static void +proto_tree_set_ax25_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + proto_tree_set_ax25(fi, tvb_get_ptr(tvb, start, 7)); +} + + +/* Add a FT_ETHER to a proto_tree */ +proto_item * +proto_tree_add_ether(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_ETHER); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_ether(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ether_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint8* value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_ETHER value */ +static void +proto_tree_set_ether(field_info *fi, const guint8* value) +{ + fvalue_set(&fi->value, (gpointer) value, FALSE); +} + +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + proto_tree_set_ether(fi, tvb_get_ptr(tvb, start, FT_ETHER_LEN)); +} + +/* Add a FT_BOOLEAN to a proto_tree */ +proto_item * +proto_tree_add_boolean(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_BOOLEAN); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_boolean(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_boolean_format_value(proto_tree *tree, int hfindex, + tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_BOOLEAN value */ +static void +proto_tree_set_boolean(field_info *fi, guint32 value) +{ + proto_tree_set_uint(fi, value); +} + +/* Add a FT_FLOAT to a proto_tree */ +proto_item * +proto_tree_add_float(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, float value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_FLOAT); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_float(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_float_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, float value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_float_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, float value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_FLOAT value */ +static void +proto_tree_set_float(field_info *fi, float value) +{ + fvalue_set_floating(&fi->value, value); +} + +/* Add a FT_DOUBLE to a proto_tree */ +proto_item * +proto_tree_add_double(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_DOUBLE); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_double(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_double_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, double value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, double value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_DOUBLE value */ +static void +proto_tree_set_double(field_info *fi, double value) +{ + fvalue_set_floating(&fi->value, value); +} + +/* Add FT_UINT{8,16,24,32} to a proto_tree */ +proto_item * +proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + switch (hfinfo->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, + &new_fi); + proto_tree_set_uint(new_fi, value); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + + return pi; +} + +proto_item * +proto_tree_add_uint_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_UINT{8,16,24,32} value */ +static void +proto_tree_set_uint(field_info *fi, guint32 value) +{ + header_field_info *hfinfo; + guint32 integer; + + hfinfo = fi->hfinfo; + integer = value; + + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + integer &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + integer >>= hfinfo->bitshift; + } + } + + fvalue_set_uinteger(&fi->value, integer); +} + +/* Add FT_UINT64 to a proto_tree */ +proto_item * +proto_tree_add_uint64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint64 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_UINT64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_uint64(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_uint64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_uint64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Add FT_INT{8,16,24,32} to a proto_tree */ +proto_item * +proto_tree_add_int(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + switch (hfinfo->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, + &new_fi); + proto_tree_set_int(new_fi, value); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + + return pi; +} + +proto_item * +proto_tree_add_int_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint32 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_INT{8,16,24,32} value */ +static void +proto_tree_set_int(field_info *fi, gint32 value) +{ + header_field_info *hfinfo; + guint32 integer; + + hfinfo = fi->hfinfo; + integer = (guint32) value; + + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + integer &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + integer >>= hfinfo->bitshift; + } + } + + fvalue_set_sinteger(&fi->value, integer); +} + +/* Add FT_INT64 to a proto_tree */ +proto_item * +proto_tree_add_int64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint64 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_INT64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_uint64(new_fi, (guint64)value); + + return pi; +} + +proto_item * +proto_tree_add_int64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_int64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_int64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} +/* Add a FT_EUI64 to a proto_tree */ +proto_item * +proto_tree_add_eui64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint64 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo); + + DISSECTOR_ASSERT(hfinfo->type == FT_EUI64); + + pi = proto_tree_add_pi(tree, hfinfo, tvb, start, &length, &new_fi); + proto_tree_set_eui64(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_eui64_format_value(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_eui64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + va_start(ap, format); + proto_tree_set_representation_value(pi, format, ap); + va_end(ap); + } + + return pi; +} + +proto_item * +proto_tree_add_eui64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, const guint64 value, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_eui64(tree, hfindex, tvb, start, length, value); + if (pi != tree) { + TRY_TO_FAKE_THIS_REPR(pi); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + } + + return pi; +} + +/* Set the FT_EUI64 value */ +static void +proto_tree_set_eui64(field_info *fi, const guint64 value) +{ + fvalue_set_integer64(&fi->value, value); +} +static void +proto_tree_set_eui64_tvb(field_info *fi, tvbuff_t *tvb, gint start, const guint encoding) +{ + if (encoding) + { + proto_tree_set_eui64(fi, tvb_get_letoh64(tvb, start)); + } else { + proto_tree_set_eui64(fi, tvb_get_ntoh64(tvb, start)); + } +} + +/* Add a field_info struct to the proto_tree, encapsulating it in a proto_node */ +static proto_item * +proto_tree_add_node(proto_tree *tree, field_info *fi) +{ + proto_node *pnode, *tnode, *sibling; + field_info *tfi; + + /* + * Make sure "tree" is ready to have subtrees under it, by + * checking whether it's been given an ett_ value. + * + * "PNODE_FINFO(tnode)" may be null; that's the case for the root + * node of the protocol tree. That node is not displayed, + * so it doesn't need an ett_ value to remember whether it + * was expanded. + */ + tnode = tree; + tfi = PNODE_FINFO(tnode); + if (tfi != NULL && (tfi->tree_type < 0 || tfi->tree_type >= num_tree_types)) { + REPORT_DISSECTOR_BUG(ep_strdup_printf("\"%s\" - \"%s\" tfi->tree_type: %u invalid (%s:%u)", + fi->hfinfo->name, fi->hfinfo->abbrev, tfi->tree_type, __FILE__, __LINE__)); + /* XXX - is it safe to continue here? */ + } + + PROTO_NODE_NEW(pnode); + pnode->parent = tnode; + PNODE_FINFO(pnode) = fi; + pnode->tree_data = PTREE_DATA(tree); + + if (tnode->last_child != NULL) { + sibling = tnode->last_child; + DISSECTOR_ASSERT(sibling->next == NULL); + sibling->next = pnode; + } else + tnode->first_child = pnode; + tnode->last_child = pnode; + + tree_data_add_maybe_interesting_field(pnode->tree_data, fi); + + return (proto_item *)pnode; +} + + +/* Generic way to allocate field_info and add to proto_tree. + * Sets *pfi to address of newly-allocated field_info struct */ +static proto_item * +proto_tree_add_pi(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, gint start, + gint *length, field_info **pfi) +{ + proto_item *pi; + field_info *fi; + + fi = alloc_field_info(tree, hfinfo, tvb, start, length); + pi = proto_tree_add_node(tree, fi); + + *pfi = fi; + + return pi; +} + + +static void +get_hfi_and_length(header_field_info *hfinfo, tvbuff_t *tvb, const gint start, gint *length, + gint *item_length) +{ + gint length_remaining; + + /* + * We only allow a null tvbuff if the item has a zero length, + * i.e. if there's no data backing it. + */ + DISSECTOR_ASSERT(tvb != NULL || *length == 0); + + /* + * XXX - in some protocols, there are 32-bit unsigned length + * fields, so lengths in protocol tree and tvbuff routines + * should really be unsigned. We should have, for those + * field types for which "to the end of the tvbuff" makes sense, + * additional routines that take no length argument and + * add fields that run to the end of the tvbuff. + */ + if (*length == -1) { + /* + * For FT_NONE, FT_PROTOCOL, FT_BYTES, and FT_STRING fields, + * a length of -1 means "set the length to what remains in + * the tvbuff". + * + * The assumption is either that + * + * 1) the length of the item can only be determined + * by dissection (typically true of items with + * subitems, which are probably FT_NONE or + * FT_PROTOCOL) + * + * or + * + * 2) if the tvbuff is "short" (either due to a short + * snapshot length or due to lack of reassembly of + * fragments/segments/whatever), we want to display + * what's available in the field (probably FT_BYTES + * or FT_STRING) and then throw an exception later + * + * or + * + * 3) the field is defined to be "what's left in the + * packet" + * + * so we set the length to what remains in the tvbuff so + * that, if we throw an exception while dissecting, it + * has what is probably the right value. + * + * For FT_STRINGZ, it means "the string is null-terminated, + * not null-padded; set the length to the actual length + * of the string", and if the tvbuff if short, we just + * throw an exception. + * + * It's not valid for any other type of field. + */ + switch (hfinfo->type) { + + case FT_PROTOCOL: + /* + * We allow this to be zero-length - for + * example, an ONC RPC NULL procedure has + * neither arguments nor reply, so the + * payload for that protocol is empty. + * + * However, if the length is negative, the + * start offset is *past* the byte past the + * end of the tvbuff, so we throw an + * exception. + */ + *length = tvb_length_remaining(tvb, start); + if (*length < 0) { + /* + * Use "tvb_ensure_bytes_exist()" + * to force the appropriate exception + * to be thrown. + */ + tvb_ensure_bytes_exist(tvb, start, 0); + } + DISSECTOR_ASSERT(*length >= 0); + break; + + case FT_NONE: + case FT_BYTES: + case FT_STRING: + *length = tvb_ensure_length_remaining(tvb, start); + DISSECTOR_ASSERT(*length >= 0); + break; + + case FT_STRINGZ: + /* + * Leave the length as -1, so our caller knows + * it was -1. + */ + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + *item_length = *length; + } else { + *item_length = *length; + if (hfinfo->type == FT_PROTOCOL || hfinfo->type == FT_NONE) { + /* + * These types are for interior nodes of the + * tree, and don't have data associated with + * them; if the length is negative (XXX - see + * above) or goes past the end of the tvbuff, + * cut it short at the end of the tvbuff. + * That way, if this field is selected in + * Wireshark, we don't highlight stuff past + * the end of the data. + */ + /* XXX - what to do, if we don't have a tvb? */ + if (tvb) { + length_remaining = tvb_length_remaining(tvb, start); + if (*item_length < 0 || + (*item_length > 0 && + (length_remaining < *item_length))) + *item_length = length_remaining; + } + } + if (*item_length < 0) { + THROW(ReportedBoundsError); + } + } +} + +static field_info * +new_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, + const gint start, const gint item_length) +{ + field_info *fi; + + FIELD_INFO_NEW(fi); + + fi->hfinfo = hfinfo; + fi->start = start; + fi->start += (tvb)?tvb_raw_offset(tvb):0; + fi->length = item_length; + fi->tree_type = -1; + fi->flags = 0; + if (!PTREE_DATA(tree)->visible) + FI_SET_FLAG(fi, FI_HIDDEN); + fvalue_init(&fi->value, fi->hfinfo->type); + fi->rep = NULL; + + /* add the data source tvbuff */ + fi->ds_tvb = tvb ? tvb_get_ds_tvb(tvb) : NULL; + + fi->appendix_start = 0; + fi->appendix_length = 0; + + return fi; +} + +static field_info * +alloc_field_info(proto_tree *tree, header_field_info *hfinfo, tvbuff_t *tvb, const gint start, + gint *length) +{ + gint item_length; + + get_hfi_and_length(hfinfo, tvb, start, length, &item_length); + return new_field_info(tree, hfinfo, tvb, start, item_length); +} + +/* If the protocol tree is to be visible, set the representation of a + proto_tree entry with the name of the field for the item and with + the value formatted with the supplied printf-style format and + argument list. */ +static void +proto_tree_set_representation_value(proto_item *pi, const char *format, va_list ap) +{ + g_assert(pi); + + /* If the tree (GUI) or item isn't visible it's pointless for us to generate the protocol + * items string representation */ + if (PTREE_DATA(pi)->visible && !PROTO_ITEM_IS_HIDDEN(pi)) { + int ret = 0; + field_info *fi = PITEM_FINFO(pi); + header_field_info *hf; + + DISSECTOR_ASSERT(fi); + + hf = fi->hfinfo; + + ITEM_LABEL_NEW(fi->rep); + if (hf->bitmask && (hf->type == FT_BOOLEAN || IS_FT_UINT(hf->type))) { + guint32 val; + char *p; + + val = fvalue_get_uinteger(&fi->value); + if (hf->bitshift > 0) + val <<= hf->bitshift; + + p = decode_bitfield_value(fi->rep->representation, val, hf->bitmask, hfinfo_bitwidth(hf)); + ret = (int) (p - fi->rep->representation); + } + + /* put in the hf name */ + ret += g_snprintf(fi->rep->representation + ret, ITEM_LABEL_LENGTH - ret, "%s: ", hf->name); + + /* If possible, Put in the value of the string */ + if (ret < ITEM_LABEL_LENGTH) { + ret += g_vsnprintf(fi->rep->representation + ret, + ITEM_LABEL_LENGTH - ret, format, ap); + } + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user + * that the field is truncated. + */ + char *oldrep; + + /* Argh, we cannot reuse 'ap' here. So make a copy + * of what we formatted for (re)use below. + */ + oldrep = g_strdup(fi->rep->representation); + + g_snprintf(fi->rep->representation, + ITEM_LABEL_LENGTH, + "[truncated] %s", + oldrep); + g_free(oldrep); + } + } +} + +/* If the protocol tree is to be visible, set the representation of a + proto_tree entry with the representation formatted with the supplied + printf-style format and argument list. */ +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap) +{ + int ret; /*tmp return value */ + field_info *fi = PITEM_FINFO(pi); + + DISSECTOR_ASSERT(fi); + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + ITEM_LABEL_NEW(fi->rep); + ret = g_vsnprintf(fi->rep->representation, ITEM_LABEL_LENGTH, + format, ap); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user + * that the field is truncated. + */ + char *oldrep; + + /* Argh, we cannot reuse 'ap' here. So make a copy + * of what we formatted for (re)use below. + */ + oldrep = g_strdup(fi->rep->representation); + + g_snprintf(fi->rep->representation, ITEM_LABEL_LENGTH, + "[truncated] %s", oldrep); + g_free(oldrep); + } + } +} + +static int +protoo_strlcpy(gchar *dest, const gchar *src, gsize dest_size) +{ + gsize res = g_strlcpy(dest, src, dest_size); + + if (res > dest_size) + res = dest_size; + return (int) res; +} + +/* -------------------------- */ +const gchar * +proto_custom_set(proto_tree* tree, const int field_id, gint occurrence, + gchar *result, gchar *expr, const int size) +{ + guint32 u_integer; + gint32 integer; + guint8 *bytes; + ipv4_addr *ipv4; + struct e_in6_addr *ipv6; + address addr; + guint32 n_addr; /* network-order IPv4 address */ + + const true_false_string *tfstring; + + int len, prev_len = 0, last, i, offset_r = 0, offset_e = 0; + GPtrArray *finfos; + field_info *finfo = NULL; + header_field_info* hfinfo; + const gchar *abbrev = NULL; + + char number_buf[32]; + const char *number_out; + + g_assert(field_id >= 0); + + hfinfo = proto_registrar_get_nth((guint)field_id); + + /* do we need to rewind ? */ + if (!hfinfo) + return ""; + + if (occurrence < 0) { + /* Search other direction */ + while (hfinfo->same_name_prev) { + hfinfo = hfinfo->same_name_prev; + } + } + + while (hfinfo) { + finfos = proto_get_finfo_ptr_array(tree, hfinfo->id); + + if (!finfos || !(len = g_ptr_array_len(finfos))) { + if (occurrence < 0) { + hfinfo = hfinfo->same_name_next; + } else { + hfinfo = hfinfo->same_name_prev; + } + continue; + } + + /* Are there enough occurrences of the field? */ + if (((occurrence - prev_len) > len) || ((occurrence + prev_len) < -len)) { + if (occurrence < 0) { + hfinfo = hfinfo->same_name_next; + } else { + hfinfo = hfinfo->same_name_prev; + } + prev_len += len; + continue; + } + + /* Calculate single index or set outer bounderies */ + if (occurrence < 0) { + i = occurrence + len + prev_len; + last = i; + } else if (occurrence > 0) { + i = occurrence - 1 - prev_len; + last = i; + } else { + i = 0; + last = len - 1; + } + + prev_len += len; /* Count handled occurrences */ + + while (i <= last) { + finfo = (field_info *)g_ptr_array_index(finfos, i); + + if (offset_r && (offset_r < (size - 2))) + result[offset_r++] = ','; + + if (offset_e && (offset_e < (size - 2))) + expr[offset_e++] = ','; + + switch (hfinfo->type) { + + case FT_NONE: /* Nothing to add */ + if (offset_r == 0) { + result[0] = '\0'; + } else if (result[offset_r-1] == ',') { + result[offset_r-1] = '\0'; + } + break; + + case FT_PROTOCOL: + /* prevent multiple "yes" entries by setting result directly */ + g_strlcpy(result, "Yes", size); + break; + + case FT_UINT_BYTES: + case FT_BYTES: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + bytes_to_str(bytes, + fvalue_length(&finfo->value)), + size-offset_r); + break; + + case FT_ABSOLUTE_TIME: + offset_r += protoo_strlcpy(result+offset_r, + abs_time_to_str((const nstime_t *)fvalue_get(&finfo->value), + (absolute_time_display_e)hfinfo->display, TRUE), + size-offset_r); + break; + + case FT_RELATIVE_TIME: + offset_r += protoo_strlcpy(result+offset_r, + rel_time_to_secs_str((const nstime_t *)fvalue_get(&finfo->value)), + size-offset_r); + break; + + case FT_BOOLEAN: + u_integer = fvalue_get_uinteger(&finfo->value); + tfstring = (const true_false_string *)&tfs_true_false; + if (hfinfo->strings) { + tfstring = (const struct true_false_string*) hfinfo->strings; + } + offset_r += protoo_strlcpy(result+offset_r, + u_integer ? + tfstring->true_string : + tfstring->false_string, size-offset_r); + + offset_e += protoo_strlcpy(expr+offset_e, + u_integer ? "1" : "0", size-offset_e); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + u_integer = fvalue_get_uinteger(&finfo->value); + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, u_integer); + g_strlcpy(result+offset_r, tmp, size-offset_r); + } else if (hfinfo->strings) { + const char *str_val = hf_try_val_to_str(u_integer, hfinfo); + + if (str_val) + g_strlcpy(result+offset_r, str_val, size-offset_r); + else + g_snprintf(result+offset_r, size-offset_r, "%u", u_integer); + + } else { + number_out = hfinfo_number_value_format(hfinfo, number_buf, u_integer); + + g_strlcpy(result+offset_r, number_out, size-offset_r); + } + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + g_snprintf(expr+offset_e, size-offset_e, + "\"%s\"", result+offset_r); + } else { + number_out = hfinfo_numeric_value_format(hfinfo, number_buf, u_integer); + + g_strlcpy(expr+offset_e, number_out, size-offset_e); + } + + offset_r = (int)strlen(result); + offset_e = (int)strlen(expr); + break; + + case FT_INT64: + /* XXX: Should handle BASE_CUSTOM ? */ + g_snprintf(result+offset_r, size-offset_r, + "%" G_GINT64_MODIFIER "d", + fvalue_get_integer64(&finfo->value)); + offset_r = (int)strlen(result); + break; + case FT_UINT64: + g_snprintf(result+offset_r, size-offset_r, + /* XXX: Should handle BASE_CUSTOM ? */ + "%" G_GINT64_MODIFIER "u", + fvalue_get_integer64(&finfo->value)); + offset_r = (int)strlen(result); + break; + case FT_EUI64: + offset_r += protoo_strlcpy(result+offset_r, + eui64_to_str(fvalue_get_integer64(&finfo->value)), + size-offset_r); + break; + /* XXX - make these just FT_INT? */ + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + integer = fvalue_get_sinteger(&finfo->value); + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + custom_fmt_func_t fmtfunc = (custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, integer); + g_strlcpy(result+offset_r, tmp, size-offset_r); + } else if (hfinfo->strings) { + const char *str_val = hf_try_val_to_str(integer, hfinfo); + + if (str_val) + g_strlcpy(result+offset_r, str_val, size-offset_r); + else + g_snprintf(result+offset_r, size-offset_r, "%d", integer); + + } else { + number_out = hfinfo_number_value_format(hfinfo, number_buf, integer); + + g_strlcpy(result+offset_r, number_out, size-offset_r); + } + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + g_snprintf(expr+offset_e, size-offset_e, "\"%s\"", result+offset_r); + } else { + number_out = hfinfo_numeric_value_format(hfinfo, number_buf, (guint32) integer); + + g_strlcpy(expr+offset_e, number_out, size-offset_e); + } + + offset_r = (int)strlen(result); + offset_e = (int)strlen(expr); + break; + + case FT_IPv4: + ipv4 = (ipv4_addr *)fvalue_get(&finfo->value); + n_addr = ipv4_get_net_order_addr(ipv4); + offset_r += protoo_strlcpy(result+offset_r, + ip_to_str((guint8 *)&n_addr), + size-offset_r); + break; + + case FT_IPv6: + ipv6 = (struct e_in6_addr *)fvalue_get(&finfo->value); + SET_ADDRESS (&addr, AT_IPv6, sizeof(struct e_in6_addr), ipv6); + address_to_str_buf(&addr, result+offset_r, size-offset_r); + offset_r = (int)strlen(result); + break; + + case FT_ETHER: + offset_r += protoo_strlcpy(result+offset_r, + bytes_to_str_punct((const guint8 *)fvalue_get(&finfo->value), + FT_ETHER_LEN, ':'), + size-offset_r); + break; + + case FT_GUID: + offset_r += protoo_strlcpy(result+offset_r, + guid_to_str((e_guid_t *)fvalue_get(&finfo->value)), + size-offset_r); + break; + + case FT_OID: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + oid_resolved_from_encoded(bytes, + fvalue_length(&finfo->value)), + size-offset_r); + offset_e += protoo_strlcpy(expr+offset_e, + oid_encoded2string(bytes, fvalue_length(&finfo->value)), + size-offset_e); + break; + + case FT_FLOAT: + g_snprintf(result+offset_r, size-offset_r, + "%." STRINGIFY(FLT_DIG) "g", fvalue_get_floating(&finfo->value)); + offset_r = (int)strlen(result); + break; + + case FT_DOUBLE: + g_snprintf(result+offset_r, size-offset_r, + "%." STRINGIFY(DBL_DIG) "g", fvalue_get_floating(&finfo->value)); + offset_r = (int)strlen(result); + break; + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + bytes = (guint8 *)fvalue_get(&finfo->value); + offset_r += protoo_strlcpy(result+offset_r, + format_text(bytes, strlen(bytes)), + size-offset_r); + break; + + case FT_IPXNET: /*XXX really No column custom ?*/ + case FT_PCRE: + default: + g_error("hfinfo->type %d (%s) not handled\n", + hfinfo->type, + ftype_name(hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + i++; + } + + switch (hfinfo->type) { + + case FT_BOOLEAN: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_OID: + /* for these types, "expr" is filled in the loop above */ + break; + + default: + /* for all others, just copy "result" to "expr" */ + g_strlcpy(expr, result, size); + break; + } + + if (!abbrev) { + /* Store abbrev for return value */ + abbrev = hfinfo->abbrev; + } + + if (occurrence == 0) { + /* Fetch next hfinfo with same name (abbrev) */ + hfinfo = hfinfo->same_name_prev; + } else { + hfinfo = NULL; + } + } + + return abbrev ? abbrev : ""; +} + + +/* Set text of proto_item after having already been created. */ +void +proto_item_set_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + if (fi->rep) { + ITEM_LABEL_FREE(fi->rep); + fi->rep = NULL; + } + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); +} + +/* Append to text of proto_item after having already been created. */ +void +proto_item_append_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + size_t curlen; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) { + return; + } + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + /* + * If we don't already have a representation, + * generate the default representation. + */ + if (fi->rep == NULL) { + ITEM_LABEL_NEW(fi->rep); + proto_item_fill_label(fi, fi->rep->representation); + } + + curlen = strlen(fi->rep->representation); + if (ITEM_LABEL_LENGTH > curlen) { + va_start(ap, format); + g_vsnprintf(fi->rep->representation + curlen, + ITEM_LABEL_LENGTH - (gulong) curlen, format, ap); + va_end(ap); + } + } +} + +/* Prepend to text of proto_item after having already been created. */ +void +proto_item_prepend_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = NULL; + char representation[ITEM_LABEL_LENGTH]; + va_list ap; + + if (pi == NULL) { + return; + } + + fi = PITEM_FINFO(pi); + if (fi == NULL) { + return; + } + + if (!PROTO_ITEM_IS_HIDDEN(pi)) { + /* + * If we don't already have a representation, + * generate the default representation. + */ + if (fi->rep == NULL) { + ITEM_LABEL_NEW(fi->rep); + proto_item_fill_label(fi, representation); + } else + g_strlcpy(representation, fi->rep->representation, ITEM_LABEL_LENGTH); + + va_start(ap, format); + g_vsnprintf(fi->rep->representation, + ITEM_LABEL_LENGTH, format, ap); + va_end(ap); + g_strlcat(fi->rep->representation, representation, ITEM_LABEL_LENGTH); + } +} + +void +proto_item_set_len(proto_item *pi, const gint length) +{ + field_info *fi; + + if (pi == NULL) + return; + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + DISSECTOR_ASSERT(length >= 0); + fi->length = length; + + /* + * You cannot just make the "len" field of a GByteArray + * larger, if there's no data to back that length; + * you can only make it smaller. + */ + if (fi->value.ftype->ftype == FT_BYTES && length <= (gint)fi->value.value.bytes->len) + fi->value.value.bytes->len = length; +} + +/* + * Sets the length of the item based on its start and on the specified + * offset, which is the offset past the end of the item; as the start + * in the item is relative to the beginning of the data source tvbuff, + * we need to pass in a tvbuff - the end offset is relative to the beginning + * of that tvbuff. + */ +void +proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end) +{ + field_info *fi; + + if (pi == NULL) + return; + + fi = PITEM_FINFO(pi); + if (fi == NULL) + return; + + end += tvb_raw_offset(tvb); + DISSECTOR_ASSERT(end >= fi->start); + fi->length = end - fi->start; +} + +int +proto_item_get_len(const proto_item *pi) +{ + field_info *fi = PITEM_FINFO(pi); + return fi ? fi->length : -1; +} + + +/** clear flags according to the mask and set new flag values */ +#define FI_REPLACE_FLAGS(fi, mask, flags_in) { \ + (fi->flags = (fi)->flags & ~(mask)); \ + (fi->flags = (fi)->flags | (flags_in)); \ +} + +gboolean +proto_item_set_expert_flags(proto_item *pi, const int group, const guint severity) +{ + if (pi == NULL || PITEM_FINFO(pi) == NULL) + return FALSE; + + /* only change things if severity is worse or at least equal than before */ + if (severity >= FI_GET_FLAG(PITEM_FINFO(pi), PI_SEVERITY_MASK)) { + FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_GROUP_MASK, group); + FI_REPLACE_FLAGS(PITEM_FINFO(pi), PI_SEVERITY_MASK, severity); + + return TRUE; + } + + return FALSE; +} + +proto_tree * +proto_tree_create_root(packet_info *pinfo) +{ + proto_node *pnode; + + /* Initialize the proto_node */ + PROTO_NODE_NEW(pnode); + pnode->parent = NULL; + PNODE_FINFO(pnode) = NULL; + pnode->tree_data = g_new(tree_data_t, 1); + + /* Make sure we can access pinfo everywhere */ + pnode->tree_data->pinfo = pinfo; + + /* Don't initialize the tree_data_t. Wait until we know we need it */ + pnode->tree_data->interesting_hfids = NULL; + + /* Set the default to FALSE so it's easier to + * find errors; if we expect to see the protocol tree + * but for some reason the default 'visible' is not + * changed, then we'll find out very quickly. */ + pnode->tree_data->visible = FALSE; + + /* Make sure that we fake protocols (if possible) */ + pnode->tree_data->fake_protocols = TRUE; + + /* Keep track of the number of children */ + pnode->tree_data->count = 0; + + pnode->tree_data->fi_tmp = NULL; + + return (proto_tree *)pnode; +} + + +/* "prime" a proto_tree with a single hfid that a dfilter + * is interested in. */ +void +proto_tree_prime_hfid(proto_tree *tree _U_, const gint hfid) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(hfid, hfinfo); + /* this field is referenced by a filter so increase the refcount. + also increase the refcount for the parent, i.e the protocol. + */ + hfinfo->ref_type = HF_REF_TYPE_DIRECT; + /* only increase the refcount if there is a parent. + if this is a protocol and not a field then parent will be -1 + and there is no parent to add any refcounting for. + */ + if (hfinfo->parent != -1) { + header_field_info *parent_hfinfo; + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + + /* Mark parent as indirectly referenced unless it is already directly + * referenced, i.e. the user has specified the parent in a filter. + */ + if (parent_hfinfo->ref_type != HF_REF_TYPE_DIRECT) + parent_hfinfo->ref_type = HF_REF_TYPE_INDIRECT; + } +} + +proto_tree * +proto_item_add_subtree(proto_item *pi, const gint idx) { + field_info *fi; + + if (!pi) + return NULL; + + DISSECTOR_ASSERT(idx >= 0 && idx < num_tree_types); + + fi = PITEM_FINFO(pi); + if (!fi) + return (proto_tree *)pi; + + fi->tree_type = idx; + + return (proto_tree *)pi; +} + +proto_tree * +proto_item_get_subtree(const proto_item *pi) { + field_info *fi; + + if (!pi) + return NULL; + fi = PITEM_FINFO(pi); + if ( (!fi) || (fi->tree_type == -1) ) + return NULL; + return (proto_tree *)pi; +} + +proto_item * +proto_item_get_parent(const proto_item *ti) { + if (!ti) + return NULL; + return ti->parent; +} + +proto_item * +proto_item_get_parent_nth(proto_item *ti, int gen) { + if (!ti) + return NULL; + while (gen--) { + ti = ti->parent; + if (!ti) + return NULL; + } + return ti; +} + + +proto_item * +proto_tree_get_parent(const proto_tree *tree) { + if (!tree) + return NULL; + return (proto_item *)tree; +} + +proto_tree * +proto_tree_get_root(proto_tree *tree) { + if (!tree) + return NULL; + while (tree->parent) { + tree = tree->parent; + } + return tree; +} + +void +proto_tree_move_item(proto_tree *tree, proto_item *fixed_item, + proto_item *item_to_move) +{ + + /* Revert part of: http://anonsvn.wireshark.org/viewvc?view=rev&revision=32443 + * See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5500 + */ + /* This function doesn't generate any values. It only reorganizes the prococol tree + * so we can bail out immediately if it isn't visible. */ + if (!tree || !PTREE_DATA(tree)->visible) + return; + + DISSECTOR_ASSERT(item_to_move->parent == tree); + DISSECTOR_ASSERT(fixed_item->parent == tree); + + /*** cut item_to_move out ***/ + + /* is item_to_move the first? */ + if (tree->first_child == item_to_move) { + /* simply change first child to next */ + tree->first_child = item_to_move->next; + + DISSECTOR_ASSERT(tree->last_child != item_to_move); + } else { + proto_item *curr_item; + /* find previous and change it's next */ + for(curr_item = tree->first_child; curr_item != NULL; curr_item = curr_item->next) { + if (curr_item->next == item_to_move) { + break; + } + } + + DISSECTOR_ASSERT(curr_item); + + curr_item->next = item_to_move->next; + + /* fix last_child if required */ + if (tree->last_child == item_to_move) { + tree->last_child = curr_item; + } + } + + /*** insert to_move after fixed ***/ + item_to_move->next = fixed_item->next; + fixed_item->next = item_to_move; + if (tree->last_child == fixed_item) { + tree->last_child = item_to_move; + } +} + +void +proto_tree_set_appendix(proto_tree *tree, tvbuff_t *tvb, gint start, + const gint length) +{ + field_info *fi; + + if (tree == NULL) + return; + + fi = PTREE_FINFO(tree); + if (fi == NULL) + return; + + start += tvb_raw_offset(tvb); + DISSECTOR_ASSERT(start >= 0); + DISSECTOR_ASSERT(length >= 0); + + fi->appendix_start = start; + fi->appendix_length = length; +} + +int +proto_register_protocol(const char *name, const char *short_name, + const char *filter_name) +{ + protocol_t *protocol; + header_field_info *hfinfo; + int proto_id; + char *existing_name; + gint *key; + guint i; + guchar c; + gboolean found_invalid; + + /* + * Make sure there's not already a protocol with any of those + * names. Crash if there is, as that's an error in the code + * or an inappropriate plugin. + * This situation has to be fixed to not register more than one + * protocol with the same name. + * + * This is done by reducing the number of strcmp (and alike) calls + * as much as possible, as this significally slows down startup time. + * + * Drawback: As a hash value is used to reduce insert time, + * this might lead to a hash collision. + * However, although we have somewhat over 1000 protocols, we're using + * a 32 bit int so this is very, very unlikely. + */ + + key = (gint *)g_malloc (sizeof(gint)); + *key = wrs_str_hash(name); + + existing_name = (char *)g_hash_table_lookup(proto_names, key); + if (existing_name != NULL) { + /* g_error will terminate the program */ + g_error("Duplicate protocol name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", name); + } + g_hash_table_insert(proto_names, key, (gpointer)name); + + existing_name = (char *)g_hash_table_lookup(proto_short_names, (gpointer)short_name); + if (existing_name != NULL) { + g_error("Duplicate protocol short_name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", short_name); + } + g_hash_table_insert(proto_short_names, (gpointer)short_name, (gpointer)short_name); + + found_invalid = FALSE; + for (i = 0; filter_name[i]; i++) { + c = filter_name[i]; + if (!(islower(c) || isdigit(c) || c == '-' || c == '_' || c == '.')) { + found_invalid = TRUE; + } + } + if (found_invalid) { + g_error("Protocol filter name \"%s\" has one or more invalid characters." + " Allowed are lower characters, digits, '-', '_' and '.'." + " This might be caused by an inappropriate plugin or a development error.", filter_name); + } + existing_name = (char *)g_hash_table_lookup(proto_filter_names, (gpointer)filter_name); + if (existing_name != NULL) { + g_error("Duplicate protocol filter_name \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", filter_name); + } + g_hash_table_insert(proto_filter_names, (gpointer)filter_name, (gpointer)filter_name); + + /* Add this protocol to the list of known protocols; the list + is sorted by protocol short name. */ + protocol = g_new(protocol_t, 1); + protocol->name = name; + protocol->short_name = short_name; + protocol->filter_name = filter_name; + protocol->fields = NULL; + protocol->is_enabled = TRUE; /* protocol is enabled by default */ + protocol->can_toggle = TRUE; + protocol->is_private = FALSE; + /* list will be sorted later by name, when all protocols completed registering */ + protocols = g_list_prepend(protocols, protocol); + + /* Here we allocate a new header_field_info struct */ + hfinfo = g_slice_new(header_field_info); + hfinfo->name = name; + hfinfo->abbrev = filter_name; + hfinfo->type = FT_PROTOCOL; + hfinfo->display = BASE_NONE; + hfinfo->strings = protocol; + hfinfo->bitmask = 0; + hfinfo->bitshift = 0; + hfinfo->ref_type = HF_REF_TYPE_NONE; + hfinfo->blurb = NULL; + hfinfo->parent = -1; /* this field differentiates protos and fields */ + + proto_id = proto_register_field_init(hfinfo, hfinfo->parent); + protocol->proto_id = proto_id; + return proto_id; +} + +void +proto_mark_private(const int proto_id) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + if (protocol) + protocol->is_private = TRUE; +} + +gboolean +proto_is_private(const int proto_id) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + if (protocol) + return protocol->is_private; + else + return FALSE; +} + +/* + * Routines to use to iterate over the protocols. + * The argument passed to the iterator routines is an opaque cookie to + * their callers; it's the GList pointer for the current element in + * the list. + * The ID of the protocol is returned, or -1 if there is no protocol. + */ +int +proto_get_first_protocol(void **cookie) +{ + protocol_t *protocol; + + if (protocols == NULL) + return -1; + *cookie = protocols; + protocol = (protocol_t *)protocols->data; + return protocol->proto_id; +} + +int +proto_get_data_protocol(void *cookie) +{ + GList *list_item = (GList *)cookie; + + protocol_t *protocol = (protocol_t *)list_item->data; + return protocol->proto_id; +} + +int +proto_get_next_protocol(void **cookie) +{ + GList *list_item = (GList *)*cookie; + protocol_t *protocol; + + list_item = g_list_next(list_item); + if (list_item == NULL) + return -1; + *cookie = list_item; + protocol = (protocol_t *)list_item->data; + return protocol->proto_id; +} + +header_field_info * +proto_get_first_protocol_field(const int proto_id, void **cookie) +{ + protocol_t *protocol = find_protocol_by_id(proto_id); + hf_register_info *ptr; + + if ((protocol == NULL) || (protocol->fields == NULL)) + return NULL; + + *cookie = protocol->fields; + ptr = (hf_register_info *)protocol->fields->data; + return &ptr->hfinfo; +} + +header_field_info * +proto_get_next_protocol_field(void **cookie) +{ + GList *list_item = (GList *)*cookie; + hf_register_info *ptr; + + list_item = g_list_next(list_item); + if (list_item == NULL) + return NULL; + + *cookie = list_item; + ptr = (hf_register_info *)list_item->data; + return &ptr->hfinfo; +} + +protocol_t * +find_protocol_by_id(const int proto_id) +{ + header_field_info *hfinfo; + + if (proto_id < 0) + return NULL; + + PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo); + DISSECTOR_ASSERT(hfinfo->type == FT_PROTOCOL); + return (protocol_t *)hfinfo->strings; +} + +static gint compare_filter_name(gconstpointer proto_arg, + gconstpointer filter_name) +{ + const protocol_t *protocol = (const protocol_t *)proto_arg; + const gchar *f_name = (const gchar *)filter_name; + + return (strcmp(protocol->filter_name, f_name)); +} + +int +proto_get_id(const protocol_t *protocol) +{ + return protocol->proto_id; +} + +int proto_get_id_by_filter_name(const gchar* filter_name) +{ + GList *list_entry; + protocol_t *protocol; + + if(!filter_name){ + fprintf(stderr, "No filter name present"); + DISSECTOR_ASSERT(filter_name); + } + + list_entry = g_list_find_custom(protocols, filter_name, + compare_filter_name); + + if (list_entry == NULL) + return -1; + protocol = (protocol_t *)list_entry->data; + return protocol->proto_id; +} + +const char * +proto_get_protocol_name(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + + if (protocol == NULL) + return NULL; + return protocol->name; +} + +const char * +proto_get_protocol_short_name(const protocol_t *protocol) +{ + if (protocol == NULL) + return "(none)"; + return protocol->short_name; +} + +const char * +proto_get_protocol_long_name(const protocol_t *protocol) +{ + if (protocol == NULL) + return "(none)"; + return protocol->name; +} + +const char * +proto_get_protocol_filter_name(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + if (protocol == NULL) + return "(none)"; + return protocol->filter_name; +} + +gboolean +proto_is_protocol_enabled(const protocol_t *protocol) +{ + return protocol->is_enabled; +} + +gboolean +proto_can_toggle_protocol(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + return protocol->can_toggle; +} + +void +proto_set_decoding(const int proto_id, const gboolean enabled) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + DISSECTOR_ASSERT(protocol->can_toggle); + protocol->is_enabled = enabled; +} + +void +proto_enable_all(void) +{ + protocol_t *protocol; + GList *list_item = protocols; + + if (protocols == NULL) + return; + + while (list_item) { + protocol = (protocol_t *)list_item->data; + if (protocol->can_toggle) + protocol->is_enabled = TRUE; + list_item = g_list_next(list_item); + } +} + +void +proto_set_cant_toggle(const int proto_id) +{ + protocol_t *protocol; + + protocol = find_protocol_by_id(proto_id); + protocol->can_toggle = FALSE; +} + +/* for use with static arrays only, since we don't allocate our own copies +of the header_field_info struct contained within the hf_register_info struct */ +void +proto_register_field_array(const int parent, hf_register_info *hf, const int num_records) +{ + int field_id, i; + hf_register_info *ptr = hf; + protocol_t *proto; + + proto = find_protocol_by_id(parent); + for (i = 0; i < num_records; i++, ptr++) { + /* + * Make sure we haven't registered this yet. + * Most fields have variables associated with them + * that are initialized to -1; some have array elements, + * or possibly uninitialized variables, so we also allow + * 0 (which is unlikely to be the field ID we get back + * from "proto_register_field_init()"). + */ + if (*ptr->p_id != -1 && *ptr->p_id != 0) { + fprintf(stderr, + "Duplicate field detected in call to proto_register_field_array: %s is already registered\n", + ptr->hfinfo.abbrev); + return; + } + + if (proto != NULL) { + if (proto->fields == NULL) { + proto->fields = g_list_append(NULL, ptr); + proto->last_field = proto->fields; + } else { + proto->last_field = + g_list_append(proto->last_field, ptr)->next; + } + } + field_id = proto_register_field_init(&ptr->hfinfo, parent); + *ptr->p_id = field_id; + } +} + +/* unregister already registered fields */ +void +proto_unregister_field (const int parent, gint hf_id) +{ + hf_register_info *hf; + protocol_t *proto; + GList *field; + + if (hf_id == -1 || hf_id == 0) + return; + + proto = find_protocol_by_id (parent); + if (!proto || !proto->fields) { + return; + } + + for (field = g_list_first (proto->fields); field; field = g_list_next (field)) { + hf = (hf_register_info *)field->data; + if (*hf->p_id == hf_id) { + /* Found the hf_id in this protocol */ + g_tree_steal (gpa_name_tree, hf->hfinfo.abbrev); + proto->fields = g_list_remove_link (proto->fields, field); + proto->last_field = g_list_last (proto->fields); + break; + } + } +} + +/* chars allowed in field abbrev */ +static +const guchar fld_abbrev_chars[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, /* 0x20-0x2F '-', '.' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30-0x3F '0'-'9' */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40-0x4F 'A'-'O' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50-0x5F 'P'-'Z', '_' */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60-0x6F 'a'-'o' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70-0x7F 'p'-'z' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0-0xAF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0-0xBF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0-0xCF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0-0xDF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0-0xEF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0-0xFF */ +}; + +static const value_string hf_types[] = { + { FT_NONE, "FT_NONE" }, + { FT_PROTOCOL, "FT_PROTOCOL" }, + { FT_BOOLEAN, "FT_BOOLEAN" }, + { FT_UINT8, "FT_UINT8" }, + { FT_UINT16, "FT_UINT16" }, + { FT_UINT24, "FT_UINT24" }, + { FT_UINT32, "FT_UINT32" }, + { FT_UINT64, "FT_UINT64" }, + { FT_INT8, "FT_INT8" }, + { FT_INT16, "FT_INT16" }, + { FT_INT24, "FT_INT24" }, + { FT_INT32, "FT_INT32" }, + { FT_INT64, "FT_INT64" }, + { FT_EUI64, "FT_EUI64" }, + { FT_FLOAT, "FT_FLOAT" }, + { FT_DOUBLE, "FT_DOUBLE" }, + { FT_ABSOLUTE_TIME, "FT_ABSOLUTE_TIME" }, + { FT_RELATIVE_TIME, "FT_RELATIVE_TIME" }, + { FT_STRING, "FT_STRING" }, + { FT_STRINGZ, "FT_STRINGZ" }, + { FT_UINT_STRING, "FT_UINT_STRING" }, + { FT_ETHER, "FT_ETHER" }, + { FT_BYTES, "FT_BYTES" }, + { FT_UINT_BYTES, "FT_UINT_BYTES" }, + { FT_IPv4, "FT_IPv4" }, + { FT_IPv6, "FT_IPv6" }, + { FT_IPXNET, "FT_IPXNET" }, + { FT_FRAMENUM, "FT_FRAMENUM" }, + { FT_PCRE, "FT_PCR" }, + { FT_GUID, "FT_GUID" }, + { FT_OID, "FT_OID" }, + { 0, NULL } }; + +static const value_string hf_display[] = { + { BASE_NONE, "BASE_NONE" }, + { BASE_DEC, "BASE_DEC" }, + { BASE_HEX, "BASE_HEX" }, + { BASE_OCT, "BASE_OCT" }, + { BASE_DEC_HEX, "BASE_DEC_HEX" }, + { BASE_HEX_DEC, "BASE_HEX_DEC" }, + { BASE_CUSTOM, "BASE_CUSTOM" }, + { BASE_NONE|BASE_RANGE_STRING, "BASE_NONE|BASE_RANGE_STRING" }, + { BASE_DEC|BASE_RANGE_STRING, "BASE_DEC|BASE_RANGE_STRING" }, + { BASE_HEX|BASE_RANGE_STRING, "BASE_HEX|BASE_RANGE_STRING" }, + { BASE_OCT|BASE_RANGE_STRING, "BASE_OCT|BASE_RANGE_STRING" }, + { BASE_DEC_HEX|BASE_RANGE_STRING, "BASE_DEC_HEX|BASE_RANGE_STRING" }, + { BASE_HEX_DEC|BASE_RANGE_STRING, "BASE_HEX_DEC|BASE_RANGE_STRING" }, + { BASE_CUSTOM|BASE_RANGE_STRING, "BASE_CUSTOM|BASE_RANGE_STRING" }, + { ABSOLUTE_TIME_LOCAL, "ABSOLUTE_TIME_LOCAL" }, + { ABSOLUTE_TIME_UTC, "ABSOLUTE_TIME_UTC" }, + { ABSOLUTE_TIME_DOY_UTC, "ABSOLUTE_TIME_DOY_UTC" }, + { 0, NULL } }; + +/* temporary function containing assert part for easier profiling */ +static void +tmp_fld_check_assert(header_field_info *hfinfo) +{ + + /* The field must have a name (with length > 0) */ + if (!hfinfo->name || !hfinfo->name[0]) { + if (hfinfo->abbrev) + /* Try to identify the field */ + g_error("Field (abbrev='%s') does not have a name\n", + hfinfo->abbrev); + else + /* Hum, no luck */ + g_error("Field does not have a name (nor an abbreviation)\n"); + } + + /* fields with an empty string for an abbreviation aren't filterable */ + if (!hfinfo->abbrev || !hfinfo->abbrev[0]) + g_error("Field '%s' does not have an abbreviation\n", hfinfo->name); + + /* These types of fields are allowed to have value_strings, + * true_false_strings or a protocol_t struct + */ + if (hfinfo->strings != NULL && !( + (hfinfo->type == FT_UINT8) || + (hfinfo->type == FT_UINT16) || + (hfinfo->type == FT_UINT24) || + (hfinfo->type == FT_UINT32) || + (hfinfo->type == FT_UINT64) || + (hfinfo->type == FT_INT8) || + (hfinfo->type == FT_INT16) || + (hfinfo->type == FT_INT24) || + (hfinfo->type == FT_INT32) || + (hfinfo->type == FT_INT64) || + (hfinfo->type == FT_BOOLEAN) || + (hfinfo->type == FT_PROTOCOL) )) + g_error("Field '%s' (%s) has a 'strings' value but is of type %s" + " (which is not allowed to have strings)\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + + /* TODO: This check may slow down startup, and output quite a few warnings. + It would be good to be able to enable this (and possibly other checks?) + in non-release builds. */ +#if 0 + /* Check for duplicate value_string values. + There are lots that have the same value *and* string, so for now only + report those that have same value but different string. */ + if ((hfinfo->strings != NULL) && + !(hfinfo->display & BASE_RANGE_STRING) && + !((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_CUSTOM) && + ( + (hfinfo->type == FT_UINT8) || + (hfinfo->type == FT_UINT16) || + (hfinfo->type == FT_UINT24) || + (hfinfo->type == FT_UINT32) || + (hfinfo->type == FT_INT8) || + (hfinfo->type == FT_INT16) || + (hfinfo->type == FT_INT24) || + (hfinfo->type == FT_INT32) || + (hfinfo->type == FT_FRAMENUM) )) { + + int n, m; + const value_string *start_values; + const value_string *current; + + if (hfinfo->display & BASE_EXT_STRING) + start_values = VALUE_STRING_EXT_VS_P(((const value_string_ext*)hfinfo->strings)); + else + start_values = (const value_string*)hfinfo->strings; + current = start_values; + + for (n=0; current; n++, current++) { + /* Drop out if we reached the end. */ + if ((current->value == 0) && (current->strptr == NULL)) { + break; + } + + /* Check value against all previous */ + for (m=0; m < n; m++) { + /* There are lots of duplicates with the same string, + so only report if different... */ + if ((start_values[m].value == current->value) && + (strcmp(start_values[m].strptr, current->strptr) != 0)) { + g_warning("Field '%s' (%s) has a conflicting entry in its" + " value_string: %u is at indices %u (%s) and %u (%s))\n", + hfinfo->name, hfinfo->abbrev, + current->value, m, start_values[m].strptr, n, current->strptr); + } + } + } + } +#endif + + + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + /* Hexadecimal and octal are, in printf() and everywhere + * else, unsigned so don't allow dissectors to register a + * signed field to be displayed unsigned. (Else how would + * we display negative values?) + */ + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_HEX: + case BASE_OCT: + case BASE_DEC_HEX: + case BASE_HEX_DEC: + g_error("Field '%s' (%s) is signed (%s) but is being displayed unsigned (%s)\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + } + /* FALL THROUGH */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_UINT64: + /* Require integral types (other than frame number, + * which is always displayed in decimal) to have a + * number base. + * If there is a strings value then this base is not + * normally used except when constructing a display + * filter for a value not found in the strings lookup. + */ + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_DEC: + case BASE_HEX: + case BASE_OCT: + case BASE_DEC_HEX: + case BASE_HEX_DEC: + case BASE_CUSTOM: /* hfinfo_numeric_value_format() treats this as decimal */ + break; + default: + g_error("Field '%s' (%s) is an integral value (%s)" + " but is being displayed as %s\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Unknown: 0x%x)")); + } + break; + + case FT_PROTOCOL: + case FT_FRAMENUM: + if (hfinfo->display != BASE_NONE) + g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + + case FT_BOOLEAN: + break; + + case FT_ABSOLUTE_TIME: + if (!(hfinfo->display == ABSOLUTE_TIME_LOCAL || + hfinfo->display == ABSOLUTE_TIME_UTC || + hfinfo->display == ABSOLUTE_TIME_DOY_UTC)) + g_error("Field '%s' (%s) is a %s but is being displayed as %s instead of as a time\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + + default: + if (hfinfo->display != BASE_NONE) + g_error("Field '%s' (%s) is an %s but is being displayed as %s instead of BASE_NONE\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)"), + val_to_str(hfinfo->display, hf_display, "(Bit count: %d)")); + if (hfinfo->bitmask != 0) + g_error("Field '%s' (%s) is an %s but has a bitmask\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + if (hfinfo->strings != NULL) + g_error("Field '%s' (%s) is an %s but has a strings value\n", + hfinfo->name, hfinfo->abbrev, + val_to_str(hfinfo->type, hf_types, "(Unknown: %d)")); + break; + } +} + +#define PROTO_PRE_ALLOC_HF_FIELDS_MEM (120000+PRE_ALLOC_EXPERT_FIELDS_MEM) +static int +proto_register_field_init(header_field_info *hfinfo, const int parent) +{ + + tmp_fld_check_assert(hfinfo); + + /* if this is a bitfield, compute bitshift */ + if (hfinfo->bitmask) { + hfinfo->bitshift = wrs_count_bitshift(hfinfo->bitmask); + } + + hfinfo->parent = parent; + hfinfo->same_name_next = NULL; + hfinfo->same_name_prev = NULL; + + /* if we always add and never delete, then id == len - 1 is correct */ + if (gpa_hfinfo.len >= gpa_hfinfo.allocated_len) { + if (!gpa_hfinfo.hfi) { + gpa_hfinfo.allocated_len = PROTO_PRE_ALLOC_HF_FIELDS_MEM; + gpa_hfinfo.hfi = (header_field_info **)g_malloc(sizeof(header_field_info *)*PROTO_PRE_ALLOC_HF_FIELDS_MEM); + } else { + gpa_hfinfo.allocated_len += 1000; + gpa_hfinfo.hfi = (header_field_info **)g_realloc(gpa_hfinfo.hfi, + sizeof(header_field_info *)*gpa_hfinfo.allocated_len); + /*g_warning("gpa_hfinfo.allocated_len %u", gpa_hfinfo.allocated_len);*/ + } + } + gpa_hfinfo.hfi[gpa_hfinfo.len] = hfinfo; + gpa_hfinfo.len++; + hfinfo->id = gpa_hfinfo.len - 1; + + /* if we have real names, enter this field in the name tree */ + if ((hfinfo->name[0] != 0) && (hfinfo->abbrev[0] != 0 )) { + + header_field_info *same_name_next_hfinfo; + guchar c; + + /* Check that the filter name (abbreviation) is legal; + * it must contain only alphanumerics, '-', "_", and ".". */ + c = wrs_check_charset(fld_abbrev_chars, hfinfo->abbrev); + if (c) { + fprintf(stderr, "Invalid character '%c' in filter name '%s'\n", c, hfinfo->abbrev); + DISSECTOR_ASSERT(!c); + } + + /* We allow multiple hfinfo's to be registered under the same + * abbreviation. This was done for X.25, as, depending + * on whether it's modulo-8 or modulo-128 operation, + * some bitfield fields may be in different bits of + * a byte, and we want to be able to refer to that field + * with one name regardless of whether the packets + * are modulo-8 or modulo-128 packets. */ + + same_name_hfinfo = NULL; + + g_tree_insert(gpa_name_tree, (gpointer) (hfinfo->abbrev), hfinfo); + /* GLIB 2.x - if it is already present + * the previous hfinfo with the same name is saved + * to same_name_hfinfo by value destroy callback */ + if (same_name_hfinfo) { + /* There's already a field with this name. + * Put it after that field in the list of + * fields with this name, then allow the code + * after this if{} block to replace the old + * hfinfo with the new hfinfo in the GTree. Thus, + * we end up with a linked-list of same-named hfinfo's, + * with the root of the list being the hfinfo in the GTree */ + same_name_next_hfinfo = + same_name_hfinfo->same_name_next; + + hfinfo->same_name_next = same_name_next_hfinfo; + if (same_name_next_hfinfo) + same_name_next_hfinfo->same_name_prev = hfinfo; + + same_name_hfinfo->same_name_next = hfinfo; + hfinfo->same_name_prev = same_name_hfinfo; + } + } + + return hfinfo->id; +} + +void +proto_register_subtree_array(gint *const *indices, const int num_indices) +{ + int i; + gint *const *ptr = indices; + + /* + * If we've already allocated the array of tree types, expand + * it; this lets plugins such as mate add tree types after + * the initial startup. (If we haven't already allocated it, + * we don't allocate it; on the first pass, we just assign + * ett values and keep track of how many we've assigned, and + * when we're finished registering all dissectors we allocate + * the array, so that we do only one allocation rather than + * wasting CPU time and memory by growing the array for each + * dissector that registers ett values.) + */ + if (tree_is_expanded != NULL) { + tree_is_expanded = (guint32 *)g_realloc(tree_is_expanded, (1+((num_tree_types + num_indices)/32)) * sizeof(guint32)); + + /* set new items to 0 */ + /* XXX, slow!!! optimize when needed (align 'i' to 32, and set rest of guint32 to 0) */ + for (i = num_tree_types; i < num_tree_types + num_indices; i++) + tree_is_expanded[i >> 5] &= ~(1 << (i & 31)); + } + + /* + * Assign "num_indices" subtree numbers starting at "num_tree_types", + * returning the indices through the pointers in the array whose + * first element is pointed to by "indices", and update + * "num_tree_types" appropriately. + */ + for (i = 0; i < num_indices; i++, ptr++, num_tree_types++) { + if (**ptr != -1) { + /* g_error will terminate the program */ + g_error("register_subtree_array: subtree item type (ett_...) not -1 !" + " This is a development error:" + " Either the subtree item type has already been assigned or" + " was not initialized to -1."); + } + **ptr = num_tree_types; + } +} + +static int +label_fill_descr(char *label_str, const header_field_info *hfinfo, const char *text, const char *descr) +{ + gint ret; + + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s (%s)", hfinfo->name, text, descr); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */ + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s [truncated]: %s (%s)", hfinfo->name, text, descr); + } + + return ret; +} + +static int +label_fill(char *label_str, const header_field_info *hfinfo, const char *text) +{ + gint ret; + + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, text); + if (ret >= ITEM_LABEL_LENGTH) { + /* Uh oh, we don't have enough room. Tell the user that the field is truncated. */ + ret = g_snprintf(label_str, ITEM_LABEL_LENGTH, "%s [truncated]: %s", hfinfo->name, text); + } + + return ret; +} + +void +proto_item_fill_label(field_info *fi, gchar *label_str) +{ + header_field_info *hfinfo; + guint8 *bytes; + guint32 integer; + guint64 integer64; + ipv4_addr *ipv4; + e_guid_t *guid; + guint32 n_addr; /* network-order IPv4 address */ + const gchar *name; + + if (!fi) { + if (label_str) + label_str[0]= '\0'; + /* XXX: Check validity of hfinfo->type */ + return; + } + + hfinfo = fi->hfinfo; + + switch (hfinfo->type) { + case FT_NONE: + case FT_PROTOCOL: + g_strlcpy(label_str, hfinfo->name, ITEM_LABEL_LENGTH); + break; + + case FT_BOOLEAN: + fill_label_boolean(fi, label_str); + break; + + case FT_BYTES: + case FT_UINT_BYTES: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, + (bytes) ? bytes_to_str(bytes, fvalue_length(&fi->value)) : ""); + break; + + /* Four types of integers to take care of: + * Bitfield, with val_string + * Bitfield, w/o val_string + * Non-bitfield, with val_string + * Non-bitfield, w/o val_string + */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + if (hfinfo->bitmask) { + fill_label_bitfield(fi, label_str); + } else { + fill_label_number(fi, label_str, FALSE); + } + break; + + case FT_FRAMENUM: + fill_label_number(fi, label_str, FALSE); + break; + + case FT_UINT64: + fill_label_number64(fi, label_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + DISSECTOR_ASSERT(!hfinfo->bitmask); + fill_label_number(fi, label_str, TRUE); + break; + + case FT_INT64: + fill_label_number64(fi, label_str, TRUE); + break; + + case FT_FLOAT: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %." STRINGIFY(FLT_DIG) "g", + hfinfo->name, fvalue_get_floating(&fi->value)); + break; + + case FT_DOUBLE: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %." STRINGIFY(DBL_DIG) "g", + hfinfo->name, fvalue_get_floating(&fi->value)); + break; + + case FT_ABSOLUTE_TIME: + label_fill(label_str, hfinfo, + abs_time_to_str((const nstime_t *)fvalue_get(&fi->value), + (absolute_time_display_e)hfinfo->display, TRUE)); + break; + + case FT_RELATIVE_TIME: + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s seconds", hfinfo->name, + rel_time_to_secs_str((const nstime_t *)fvalue_get(&fi->value))); + break; + + case FT_IPXNET: + integer = fvalue_get_uinteger(&fi->value); + g_snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%08X)", hfinfo->name, + get_ipxnet_name(integer), integer); + break; + + case FT_AX25: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_ax25_name(bytes), + ax25_to_str(bytes)); + break; + + case FT_ETHER: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_ether_name(bytes), + ether_to_str(bytes)); + break; + + case FT_IPv4: + ipv4 = (ipv4_addr *)fvalue_get(&fi->value); + n_addr = ipv4_get_net_order_addr(ipv4); + label_fill_descr(label_str, hfinfo, + get_hostname(n_addr), + ip_to_str((guint8*)&n_addr)); + break; + + case FT_IPv6: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill_descr(label_str, hfinfo, + get_hostname6((struct e_in6_addr *)bytes), + ip6_to_str((struct e_in6_addr*)bytes)); + break; + + case FT_GUID: + guid = (e_guid_t *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, guid_to_str(guid)); + break; + + case FT_OID: + bytes = (guint8 *)fvalue_get(&fi->value); + name = oid_resolved_from_encoded(bytes, fvalue_length(&fi->value)); + if (name) { + label_fill_descr(label_str, hfinfo, + oid_encoded2string(bytes, fvalue_length(&fi->value)), name); + } else { + label_fill(label_str, hfinfo, + oid_encoded2string(bytes, fvalue_length(&fi->value))); + } + break; + case FT_EUI64: + integer64 = fvalue_get_integer64(&fi->value); + label_fill_descr(label_str, hfinfo, + get_eui64_name(integer64), + eui64_to_str(integer64)); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + bytes = (guint8 *)fvalue_get(&fi->value); + label_fill(label_str, hfinfo, format_text(bytes, strlen(bytes))); + break; + + default: + g_error("hfinfo->type %d (%s) not handled\n", + hfinfo->type, ftype_name(hfinfo->type)); + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } +} + +static void +fill_label_boolean(field_info *fi, gchar *label_str) +{ + char *p = label_str; + int bitfield_byte_length = 0, bitwidth; + guint32 unshifted_value; + guint32 value; + + header_field_info *hfinfo = fi->hfinfo; + const true_false_string *tfstring = (const true_false_string *)&tfs_true_false; + + if (hfinfo->strings) { + tfstring = (const struct true_false_string*) hfinfo->strings; + } + + value = fvalue_get_uinteger(&fi->value); + if (hfinfo->bitmask) { + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Un-shift bits */ + unshifted_value = value; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = (int) (p - label_str); + } + + /* Fill in the textual info */ + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, + value ? tfstring->true_string : tfstring->false_string); +} + +static const char * +hf_try_val_to_str(guint32 value, const header_field_info *hfinfo) +{ + if (hfinfo->display & BASE_RANGE_STRING) + return try_rval_to_str(value, (const range_string *) hfinfo->strings); + + if (hfinfo->display & BASE_EXT_STRING) + return try_val_to_str_ext(value, (const value_string_ext *) hfinfo->strings); + + return try_val_to_str(value, (const value_string *) hfinfo->strings); +} + +static const char * +hf_try_val_to_str_const(guint32 value, const header_field_info *hfinfo, const char *unknown_str) +{ + const char *str = hf_try_val_to_str(value, hfinfo); + + return (str) ? str : unknown_str; +} + +/* Fills data for bitfield ints with val_strings */ +static void +fill_label_bitfield(field_info *fi, gchar *label_str) +{ + char *p; + int bitfield_byte_length, bitwidth; + guint32 unshifted_value; + guint32 value; + + char buf[32]; + const char *out; + + header_field_info *hfinfo = fi->hfinfo; + + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Un-shift bits */ + unshifted_value = fvalue_get_uinteger(&fi->value); + value = unshifted_value; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = (int) (p - label_str); + + /* Fill in the textual info using stored (shifted) value */ + if (hfinfo->display == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, value); + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, tmp); + } + else if (hfinfo->strings) { + const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown"); + + out = hfinfo_number_vals_format(hfinfo, buf, value); + if (out == NULL) /* BASE_NONE so don't put integer in descr */ + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, val_str); + else + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s (%s)", hfinfo->name, val_str, out); + } + else { + out = hfinfo_number_value_format(hfinfo, buf, value); + + g_snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, out); + } +} + +static void +fill_label_number(field_info *fi, gchar *label_str, gboolean is_signed) +{ + header_field_info *hfinfo = fi->hfinfo; + guint32 value; + + char buf[32]; + const char *out; + + if (is_signed) + value = fvalue_get_sinteger(&fi->value); + else + value = fvalue_get_uinteger(&fi->value); + + /* Fill in the textual info */ + if (hfinfo->display == BASE_CUSTOM) { + gchar tmp[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hfinfo->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(tmp, value); + label_fill(label_str, hfinfo, tmp); + } + else if (hfinfo->strings) { + const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown"); + + out = hfinfo_number_vals_format(hfinfo, buf, value); + if (out == NULL) /* BASE_NONE so don't put integer in descr */ + label_fill(label_str, hfinfo, val_str); + else + label_fill_descr(label_str, hfinfo, val_str, out); + } + else { + out = hfinfo_number_value_format(hfinfo, buf, value); + + label_fill(label_str, hfinfo, out); + } +} + +static void +fill_label_number64(field_info *fi, gchar *label_str, gboolean is_signed) +{ + const char *format = NULL; + header_field_info *hfinfo = fi->hfinfo; + guint64 value; + + /* Pick the proper format string */ + if (is_signed) + format = hfinfo_int64_format(hfinfo); + else + format = hfinfo_uint64_format(hfinfo); + + value = fvalue_get_integer64(&fi->value); + + /* Fill in the textual info */ + if (IS_BASE_DUAL(hfinfo->display)) { + g_snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, value, value); + } else { + g_snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, value); + } +} + +int +hfinfo_bitwidth(const header_field_info *hfinfo) +{ + int bitwidth = 0; + + if (!hfinfo->bitmask) { + return 0; + } + + switch (hfinfo->type) { + case FT_UINT8: + case FT_INT8: + bitwidth = 8; + break; + case FT_UINT16: + case FT_INT16: + bitwidth = 16; + break; + case FT_UINT24: + case FT_INT24: + bitwidth = 24; + break; + case FT_UINT32: + case FT_INT32: + bitwidth = 32; + break; + case FT_BOOLEAN: + bitwidth = hfinfo->display; /* hacky? :) */ + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return bitwidth; +} + +static int +_hfinfo_type_hex_octet(int type) +{ + switch (type) { + case FT_INT8: + case FT_UINT8: + return 2; + + case FT_UINT16: + case FT_INT16: + return 4; + + case FT_UINT24: + case FT_INT24: + return 6; + + case FT_UINT32: + case FT_INT32: + return 8; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return -1; +} + +/* private to_str.c API don't export to .h! */ +char *oct_to_str_back(char *ptr, guint32 value); +char *hex_to_str_back(char *ptr, int pad, guint32 value); +char *uint_to_str_back(char *ptr, guint32 value); +char *int_to_str_back(char *ptr, gint32 value); + +static const char * +_hfinfo_number_value_format(const header_field_info *hfinfo, int display, char buf[32], guint32 value) +{ + char *ptr = &buf[31]; + gboolean isint = IS_FT_INT(hfinfo->type); + + *ptr = '\0'; + /* Properly format value */ + switch (display) { + case BASE_DEC: + return isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + + case BASE_DEC_HEX: + *(--ptr) = ')'; + ptr = hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + *(--ptr) = '('; + *(--ptr) = ' '; + ptr = isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + return ptr; + + case BASE_OCT: + return oct_to_str_back(ptr, value); + + case BASE_HEX: + return hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + + case BASE_HEX_DEC: + *(--ptr) = ')'; + ptr = isint ? int_to_str_back(ptr, (gint32) value) : uint_to_str_back(ptr, value); + *(--ptr) = '('; + *(--ptr) = ' '; + ptr = hex_to_str_back(ptr, _hfinfo_type_hex_octet(hfinfo->type), value); + return ptr; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return ptr; +} + +static const char * +hfinfo_number_value_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + int display = hfinfo->display; + + if (hfinfo->type == FT_FRAMENUM) { + /* + * Frame numbers are always displayed in decimal. + */ + display = BASE_DEC; + } + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_numeric_value_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + /* Get the underlying BASE_ value */ + int display = hfinfo->display & BASE_DISPLAY_E_MASK; + + if (hfinfo->type == FT_FRAMENUM) { + /* + * Frame numbers are always displayed in decimal. + */ + display = BASE_DEC; + } + + switch (display) { + case BASE_NONE: + /* case BASE_DEC: */ + case BASE_DEC_HEX: + case BASE_OCT: /* XXX, why we're changing BASE_OCT to BASE_DEC? */ + case BASE_CUSTOM: + display = BASE_DEC; + break; + + /* case BASE_HEX: */ + case BASE_HEX_DEC: + display = BASE_HEX; + break; + } + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_number_vals_format(const header_field_info *hfinfo, char buf[32], guint32 value) +{ + /* Get the underlying BASE_ value */ + int display = hfinfo->display & BASE_DISPLAY_E_MASK; + + if (display == BASE_NONE) + return NULL; + + if (display == BASE_DEC_HEX) + display = BASE_DEC; + if (display == BASE_HEX_DEC) + display = BASE_HEX; + + return _hfinfo_number_value_format(hfinfo, display, buf, value); +} + +static const char * +hfinfo_uint64_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Pick the proper format string */ + switch (hfinfo->display) { + case BASE_DEC: + format = "%s: %" G_GINT64_MODIFIER "u"; + break; + case BASE_DEC_HEX: + format = "%s: %" G_GINT64_MODIFIER "u (0x%016" G_GINT64_MODIFIER "x)"; + break; + case BASE_OCT: /* I'm lazy */ + format = "%s: %#" G_GINT64_MODIFIER "o"; + break; + case BASE_HEX: + format = "%s: 0x%016" G_GINT64_MODIFIER "x"; + break; + case BASE_HEX_DEC: + format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "u)"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +static const char * +hfinfo_int64_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Pick the proper format string */ + switch (hfinfo->display) { + case BASE_DEC: + format = "%s: %" G_GINT64_MODIFIER "d"; + break; + case BASE_DEC_HEX: + format = "%s: %" G_GINT64_MODIFIER "d (0x%016" G_GINT64_MODIFIER "x)"; + break; + case BASE_OCT: /* I'm lazy */ + format = "%s: %#" G_GINT64_MODIFIER "o"; + break; + case BASE_HEX: + format = "%s: 0x%016" G_GINT64_MODIFIER "x"; + break; + case BASE_HEX_DEC: + format = "%s: 0x%016" G_GINT64_MODIFIER "x (%" G_GINT64_MODIFIER "d)"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +int +proto_registrar_n(void) +{ + return gpa_hfinfo.len; +} + +const char * +proto_registrar_get_name(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->name; +} + +const char * +proto_registrar_get_abbrev(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->abbrev; +} + +enum ftenum +proto_registrar_get_ftype(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->type; +} + +int +proto_registrar_get_parent(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return hfinfo->parent; +} + +gboolean +proto_registrar_is_protocol(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return (hfinfo->parent == -1 ? TRUE : FALSE); +} + +/* Returns length of field in packet (not necessarily the length + * in our internal representation, as in the case of IPv4). + * 0 means undeterminable at time of registration + * -1 means the field is not registered. */ +gint +proto_registrar_get_length(const int n) +{ + header_field_info *hfinfo; + + PROTO_REGISTRAR_GET_NTH(n, hfinfo); + return ftype_length(hfinfo->type); +} + +/* Looks for a protocol or a field in a proto_tree. Returns TRUE if + * it exists anywhere, or FALSE if it exists nowhere. */ +gboolean +proto_check_for_protocol_or_field(const proto_tree* tree, const int id) +{ + GPtrArray *ptrs = proto_get_finfo_ptr_array(tree, id); + + if (!ptrs) { + return FALSE; + } + else if (g_ptr_array_len(ptrs) > 0) { + return TRUE; + } + else { + return FALSE; + } +} + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in tree. + * This only works if the hfindex was "primed" before the dissection + * took place, as we just pass back the already-created GPtrArray*. + * The caller should *not* free the GPtrArray*; proto_tree_free_node() + * handles that. */ +GPtrArray * +proto_get_finfo_ptr_array(const proto_tree *tree, const int id) +{ + if (!tree) + return NULL; + + if (PTREE_DATA(tree)->interesting_hfids != NULL) + return (GPtrArray *)g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids, + GINT_TO_POINTER(id)); + else + return NULL; +} + +gboolean +proto_tracking_interesting_fields(const proto_tree *tree) +{ + if (!tree) + return FALSE; + + return (PTREE_DATA(tree)->interesting_hfids != NULL); +} + +/* Helper struct for proto_find_info() and proto_all_finfos() */ +typedef struct { + GPtrArray *array; + int id; +} ffdata_t; + +/* Helper function for proto_find_info() */ +static gboolean +find_finfo(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + if (fi && fi->hfinfo) { + if (fi->hfinfo->id == ((ffdata_t*)data)->id) { + g_ptr_array_add(((ffdata_t*)data)->array, fi); + } + } + + /* Don't stop traversing. */ + return FALSE; +} + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in a tree. +* This works on any proto_tree, primed or unprimed, but actually searches +* the tree, so it is slower than using proto_get_finfo_ptr_array on a primed tree. +* The caller does need to free the returned GPtrArray with +* g_ptr_array_free(, TRUE). +*/ +GPtrArray * +proto_find_finfo(proto_tree *tree, const int id) +{ + ffdata_t ffdata; + + ffdata.array = g_ptr_array_new(); + ffdata.id = id; + + proto_tree_traverse_pre_order(tree, find_finfo, &ffdata); + + return ffdata.array; +} + +/* Helper function for proto_all_finfos() */ +static gboolean +every_finfo(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + if (fi && fi->hfinfo) { + g_ptr_array_add(((ffdata_t*)data)->array, fi); + } + + /* Don't stop traversing. */ + return FALSE; +} + +/* Return GPtrArray* of field_info pointers containing all hfindexes that appear in a tree. */ +GPtrArray * +proto_all_finfos(proto_tree *tree) +{ + ffdata_t ffdata; + + ffdata.array = g_ptr_array_new(); + ffdata.id = 0; + + proto_tree_traverse_pre_order(tree, every_finfo, &ffdata); + + return ffdata.array; +} + + +typedef struct { + guint offset; + field_info *finfo; + tvbuff_t *tvb; +} offset_search_t; + +static gboolean +check_for_offset(proto_node *node, const gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + offset_search_t *offsearch = (offset_search_t *)data; + + /* !fi == the top most container node which holds nothing */ + if (fi && !PROTO_ITEM_IS_HIDDEN(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) { + if (offsearch->offset >= (guint) fi->start && + offsearch->offset < (guint) (fi->start + fi->length)) { + + offsearch->finfo = fi; + return FALSE; /* keep traversing */ + } + } + return FALSE; /* keep traversing */ +} + +/* Search a proto_tree backwards (from leaves to root) looking for the field + * whose start/length occupies 'offset' */ +/* XXX - I couldn't find an easy way to search backwards, so I search + * forwards, w/o stopping. Therefore, the last finfo I find will the be + * the one I want to return to the user. This algorithm is inefficient + * and could be re-done, but I'd have to handle all the children and + * siblings of each node myself. When I have more time I'll do that. + * (yeah right) */ +field_info * +proto_find_field_from_offset(proto_tree *tree, guint offset, tvbuff_t *tvb) +{ + offset_search_t offsearch; + + offsearch.offset = offset; + offsearch.finfo = NULL; + offsearch.tvb = tvb; + + proto_tree_traverse_pre_order(tree, check_for_offset, &offsearch); + + return offsearch.finfo; +} + +/* Dumps the protocols in the registration database to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. The fields are tab-delimited. + * + * Field 1 = protocol name + * Field 2 = protocol short name + * Field 3 = protocol filter name + */ +void +proto_registrar_dump_protocols(void) +{ + protocol_t *protocol; + int i; + void *cookie = NULL; + + + i = proto_get_first_protocol(&cookie); + while (i != -1) { + protocol = find_protocol_by_id(i); + printf("%s\t%s\t%s\n", protocol->name, protocol->short_name, + protocol->filter_name); + i = proto_get_next_protocol(&cookie); + } +} + +/* Dumps the value_strings, extended value string headers, range_strings + * or true/false strings for fields that have them. + * There is one record per line. Fields are tab-delimited. + * There are four types of records: Value String, Extended Value String Header, + * Range String and True/False String. The first field, 'V', 'E', 'R' or 'T', indicates + * the type of record. + * + * Note that a record will be generated only if the value_string,... is referenced + * in a registered hfinfo entry. + * + * + * Value Strings + * ------------- + * Field 1 = 'V' + * Field 2 = Field abbreviation to which this value string corresponds + * Field 3 = Integer value + * Field 4 = String + * + * Extended Value String Headers + * ----------------------------- + * Field 1 = 'E' + * Field 2 = Field abbreviation to which this extended value string header corresponds + * Field 3 = Extended Value String "Name" + * Field 4 = Number of entries in the associated value_string array + * Field 5 = Access Type: "Linear Search", "Binary Search", "Direct (indexed) Access" + * + * Range Strings + * ------------- + * Field 1 = 'R' + * Field 2 = Field abbreviation to which this range string corresponds + * Field 3 = Integer value: lower bound + * Field 4 = Integer value: upper bound + * Field 5 = String + * + * True/False Strings + * ------------------ + * Field 1 = 'T' + * Field 2 = Field abbreviation to which this true/false string corresponds + * Field 3 = True String + * Field 4 = False String + */ +void +proto_registrar_dump_values(void) +{ + header_field_info *hfinfo; + int i, len, vi; + const value_string *vals; + const range_string *range; + const true_false_string *tfs; + + len = gpa_hfinfo.len; + for (i = 0; i < len ; i++) { + PROTO_REGISTRAR_GET_NTH(i, hfinfo); + + if (hfinfo->id == hf_text_only) { + continue; + } + + /* ignore protocols */ + if (proto_registrar_is_protocol(i)) { + continue; + } + /* process header fields */ + else { + /* + * If this field isn't at the head of the list of + * fields with this name, skip this field - all + * fields with the same name are really just versions + * of the same field stored in different bits, and + * should have the same type/radix/value list, and + * just differ in their bit masks. (If a field isn't + * a bitfield, but can be, say, 1 or 2 bytes long, + * it can just be made FT_UINT16, meaning the + * *maximum* length is 2 bytes, and be used + * for all lengths.) + */ + if (hfinfo->same_name_prev != NULL) + continue; + + vals = NULL; + range = NULL; + tfs = NULL; + + if (hfinfo->strings != NULL) { + if ((hfinfo->display & BASE_DISPLAY_E_MASK) != BASE_CUSTOM && + (hfinfo->type == FT_UINT8 || + hfinfo->type == FT_UINT16 || + hfinfo->type == FT_UINT24 || + hfinfo->type == FT_UINT32 || + hfinfo->type == FT_UINT64 || + hfinfo->type == FT_INT8 || + hfinfo->type == FT_INT16 || + hfinfo->type == FT_INT24 || + hfinfo->type == FT_INT32 || + hfinfo->type == FT_INT64)) { + + if (hfinfo->display & BASE_EXT_STRING) { + vals = VALUE_STRING_EXT_VS_P((const value_string_ext *)hfinfo->strings); + } else if ((hfinfo->display & BASE_RANGE_STRING) == 0) { + vals = (const value_string *)hfinfo->strings; + } else { + range = (const range_string *)hfinfo->strings; + } + } + else if (hfinfo->type == FT_BOOLEAN) { + tfs = (const struct true_false_string *)hfinfo->strings; + } + } + + /* Print value strings? */ + if (vals) { + if (hfinfo->display & BASE_EXT_STRING) { + const value_string_ext *vse_p = (const value_string_ext *)hfinfo->strings; + if (!value_string_ext_validate(vse_p)) { + g_warning("Invalid value_string_ext ptr for: %s", hfinfo->abbrev); + continue; + } + try_val_to_str_ext(0, vse_p); /* "prime" the extended value_string */ + printf("E\t%s\t%d\t%s\t%s\n", + hfinfo->abbrev, + VALUE_STRING_EXT_VS_NUM_ENTRIES(vse_p), + VALUE_STRING_EXT_VS_NAME(vse_p), + value_string_ext_match_type_str(vse_p)); + } + vi = 0; + while (vals[vi].strptr) { + /* Print in the proper base */ + if (hfinfo->display == BASE_HEX) { + printf("V\t%s\t0x%x\t%s\n", + hfinfo->abbrev, + vals[vi].value, + vals[vi].strptr); + } + else { + printf("V\t%s\t%u\t%s\n", + hfinfo->abbrev, + vals[vi].value, + vals[vi].strptr); + } + vi++; + } + } + + /* print range strings? */ + else if (range) { + vi = 0; + while (range[vi].strptr) { + /* Print in the proper base */ + if ((hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_HEX) { + printf("R\t%s\t0x%x\t0x%x\t%s\n", + hfinfo->abbrev, + range[vi].value_min, + range[vi].value_max, + range[vi].strptr); + } + else { + printf("R\t%s\t%u\t%u\t%s\n", + hfinfo->abbrev, + range[vi].value_min, + range[vi].value_max, + range[vi].strptr); + } + vi++; + } + } + + /* Print true/false strings? */ + else if (tfs) { + printf("T\t%s\t%s\t%s\n", hfinfo->abbrev, + tfs->true_string, tfs->false_string); + } + } + } +} + +/* Dumps the contents of the registration database to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. Each record is either a protocol or a header + * field, differentiated by the first field. The fields are tab-delimited. + * + * Protocols + * --------- + * Field 1 = 'P' + * Field 2 = descriptive protocol name + * Field 3 = protocol abbreviation + * + * Header Fields + * ------------- + * Field 1 = 'F' + * Field 2 = descriptive field name + * Field 3 = field abbreviation + * Field 4 = type ( textual representation of the the ftenum type ) + * Field 5 = parent protocol abbreviation + * Field 6 = base for display (for integer types); "parent bitfield width" for FT_BOOLEAN + * Field 7 = bitmask: format: hex: 0x.... + * Field 8 = blurb describing field + */ +void +proto_registrar_dump_fields(void) +{ + header_field_info *hfinfo, *parent_hfinfo; + int i, len; + const char *enum_name; + const char *base_name; + const char *blurb; + char width[5]; + + len = gpa_hfinfo.len; + for (i = 0; i < len ; i++) { + PROTO_REGISTRAR_GET_NTH(i, hfinfo); + + /* + * Skip the pseudo-field for "proto_tree_add_text()" since + * we don't want it in the list of filterable fields. + */ + if (hfinfo->id == hf_text_only) + continue; + + /* format for protocols */ + if (proto_registrar_is_protocol(i)) { + printf("P\t%s\t%s\n", hfinfo->name, hfinfo->abbrev); + } + /* format for header fields */ + else { + /* + * If this field isn't at the head of the list of + * fields with this name, skip this field - all + * fields with the same name are really just versions + * of the same field stored in different bits, and + * should have the same type/radix/value list, and + * just differ in their bit masks. (If a field isn't + * a bitfield, but can be, say, 1 or 2 bytes long, + * it can just be made FT_UINT16, meaning the + * *maximum* length is 2 bytes, and be used + * for all lengths.) + */ + if (hfinfo->same_name_prev != NULL) + continue; + + PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); + + enum_name = ftype_name(hfinfo->type); + base_name = ""; + + if (hfinfo->type == FT_UINT8 || + hfinfo->type == FT_UINT16 || + hfinfo->type == FT_UINT24 || + hfinfo->type == FT_UINT32 || + hfinfo->type == FT_UINT64 || + hfinfo->type == FT_INT8 || + hfinfo->type == FT_INT16 || + hfinfo->type == FT_INT24 || + hfinfo->type == FT_INT32 || + hfinfo->type == FT_INT64) { + + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_NONE: + base_name = "BASE_NONE"; + break; + case BASE_DEC: + base_name = "BASE_DEC"; + break; + case BASE_HEX: + base_name = "BASE_HEX"; + break; + case BASE_OCT: + base_name = "BASE_OCT"; + break; + case BASE_DEC_HEX: + base_name = "BASE_DEC_HEX"; + break; + case BASE_HEX_DEC: + base_name = "BASE_HEX_DEC"; + break; + case BASE_CUSTOM: + base_name = "BASE_CUSTOM"; + break; + default: + base_name = "????"; + break; + } + } else if (hfinfo->type == FT_BOOLEAN) { + /* For FT_BOOLEAN: 'display' can be "parent bitfield width" */ + g_snprintf(width, sizeof(width), "%d", hfinfo->display); + base_name = width; + } + + blurb = hfinfo->blurb; + if (blurb == NULL) + blurb = ""; + else if (strlen(blurb) == 0) + blurb = "\"\""; + + printf("F\t%s\t%s\t%s\t%s\t%s\t0x%x\t%s\n", + hfinfo->name, hfinfo->abbrev, enum_name, + parent_hfinfo->abbrev, base_name, hfinfo->bitmask, blurb); + } + } +} + +/* Dumps field types and descriptive names to stdout. An independent + * program can take this output and format it into nice tables or HTML or + * whatever. + * + * There is one record per line. The fields are tab-delimited. + * + * Field 1 = field type name, e.g. FT_UINT8 + * Field 2 = descriptive name, e.g. "Unsigned, 1 byte" + */ +void +proto_registrar_dump_ftypes(void) +{ + int fte; + + for (fte = 0; fte < FT_NUM_TYPES; fte++) { + printf("%s\t%s\n", ftype_name((ftenum_t)fte), ftype_pretty_name((ftenum_t)fte)); + } +} + +static const char * +hfinfo_numeric_format(const header_field_info *hfinfo) +{ + const char *format = NULL; + + /* Get the underlying BASE_ value */ + switch (hfinfo->display & BASE_DISPLAY_E_MASK) { + case BASE_DEC: + case BASE_DEC_HEX: + case BASE_OCT: /* I'm lazy */ + case BASE_CUSTOM: + switch (hfinfo->type) { + case FT_UINT64: + format = "%s == %" G_GINT64_MODIFIER "u"; + break; + case FT_INT64: + format = "%s == %" G_GINT64_MODIFIER "d"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + break; + case BASE_HEX: + case BASE_HEX_DEC: + switch (hfinfo->type) { + case FT_UINT64: + case FT_INT64: + format = "%s == 0x%016" G_GINT64_MODIFIER "x"; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + ; + } + return format; +} + +/* This function indicates whether it's possible to construct a + * "match selected" display filter string for the specified field, + * returns an indication of whether it's possible, and, if it's + * possible and "filter" is non-null, constructs the filter and + * sets "*filter" to point to it. + * You do not need to [g_]free() this string since it will be automatically + * freed once the next packet is dissected. + */ +static gboolean +construct_match_selected_string(field_info *finfo, epan_dissect_t *edt, + char **filter) +{ + header_field_info *hfinfo; + int abbrev_len; + char *ptr; + int buf_len; + int dfilter_len, i; + gint start, length, length_remaining; + guint8 c; + gchar is_signed_num = FALSE; + + if (!finfo) + return FALSE; + + hfinfo = finfo->hfinfo; + DISSECTOR_ASSERT(hfinfo); + abbrev_len = (int) strlen(hfinfo->abbrev); + + if (hfinfo->strings && (hfinfo->display & BASE_DISPLAY_E_MASK) == BASE_NONE) { + const gchar *str = NULL; + + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + str = hf_try_val_to_str(fvalue_get_sinteger(&finfo->value), hfinfo); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + str = hf_try_val_to_str(fvalue_get_uinteger(&finfo->value), hfinfo); + break; + + default: + break; + } + + if (str != NULL && filter != NULL) { + *filter = ep_strdup_printf("%s == \"%s\"", hfinfo->abbrev, str); + return TRUE; + } + } + + /* + * XXX - we can't use the "val_to_string_repr" and "string_repr_len" + * functions for FT_UINT and FT_INT types, as we choose the base in + * the string expression based on the display base of the field. + * + * Note that the base does matter, as this is also used for + * the protocolinfo tap. + * + * It might be nice to use them in "proto_item_fill_label()" + * as well, although, there, you'd have to deal with the base + * *and* with resolved values for addresses. + * + * Perhaps we need two different val_to_string routines, one + * to generate items for display filters and one to generate + * strings for display, and pass to both of them the + * "display" and "strings" values in the header_field_info + * structure for the field, so they can get the base and, + * if the field is Boolean or an enumerated integer type, + * the tables used to generate human-readable values. + */ + switch (hfinfo->type) { + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + is_signed_num = TRUE; + /* FALLTHRU */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_FRAMENUM: + if (filter != NULL) { + guint32 number; + + char buf[32]; + const char *out; + + if (is_signed_num) + number = fvalue_get_sinteger(&finfo->value); + else + number = fvalue_get_uinteger(&finfo->value); + + out = hfinfo_numeric_value_format(hfinfo, buf, number); + + *filter = ep_strdup_printf("%s == %s", hfinfo->abbrev, out); + } + break; + + case FT_INT64: + case FT_UINT64: + if (filter != NULL) { + const char *format = hfinfo_numeric_format(hfinfo); + + *filter = ep_strdup_printf(format, + hfinfo->abbrev, + fvalue_get_integer64(&finfo->value)); + } + break; + + case FT_PROTOCOL: + if (filter != NULL) + *filter = ep_strdup(finfo->hfinfo->abbrev); + break; + + case FT_NONE: + /* + * If the length is 0, just match the name of the + * field. + * + * (Also check for negative values, just in case, + * as we'll cast it to an unsigned value later.) + */ + length = finfo->length; + if (length == 0) { + if (filter != NULL) + *filter = ep_strdup(finfo->hfinfo->abbrev); + break; + } + if (length < 0) + return FALSE; + + /* + * This doesn't have a value, so we'd match + * on the raw bytes at this address. + * + * Should we be allowed to access to the raw bytes? + * If "edt" is NULL, the answer is "no". + */ + if (edt == NULL) + return FALSE; + + /* + * Is this field part of the raw frame tvbuff? + * If not, we can't use "frame[N:M]" to match + * it. + * + * XXX - should this be frame-relative, or + * protocol-relative? + * + * XXX - does this fallback for non-registered + * fields even make sense? + */ + if (finfo->ds_tvb != edt->tvb) + return FALSE; /* you lose */ + + /* + * Don't go past the end of that tvbuff. + */ + length_remaining = tvb_length_remaining(finfo->ds_tvb, finfo->start); + if (length > length_remaining) + length = length_remaining; + if (length <= 0) + return FALSE; + + if (filter != NULL) { + start = finfo->start; + buf_len = 32 + length * 3; + *filter = (char *)ep_alloc0(buf_len); + ptr = *filter; + + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), + "frame[%d:%d] == ", finfo->start, length); + for (i=0; ids_tvb, start); + start++; + if (i == 0 ) { + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), "%02x", c); + } + else { + ptr += g_snprintf(ptr, (gulong) (buf_len-(ptr-*filter)), ":%02x", c); + } + } + } + break; + + case FT_PCRE: + /* FT_PCRE never appears as a type for a registered field. It is + * only used internally. */ + DISSECTOR_ASSERT_NOT_REACHED(); + break; + + /* By default, use the fvalue's "to_string_repr" method. */ + default: + /* Figure out the string length needed. + * The ft_repr length. + * 4 bytes for " == ". + * 1 byte for trailing NUL. + */ + if (filter != NULL) { + dfilter_len = fvalue_string_repr_len(&finfo->value, + FTREPR_DFILTER); + dfilter_len += abbrev_len + 4 + 1; + *filter = (char *)ep_alloc0(dfilter_len); + + /* Create the string */ + g_snprintf(*filter, dfilter_len, "%s == ", + hfinfo->abbrev); + fvalue_to_string_repr(&finfo->value, + FTREPR_DFILTER, + &(*filter)[abbrev_len + 4]); + } + break; + } + + return TRUE; +} + +/* + * Returns TRUE if we can do a "match selected" on the field, FALSE + * otherwise. + */ +gboolean +proto_can_match_selected(field_info *finfo, epan_dissect_t *edt) +{ + return construct_match_selected_string(finfo, edt, NULL); +} + +/* This function attempts to construct a "match selected" display filter + * string for the specified field; if it can do so, it returns a pointer + * to the string, otherwise it returns NULL. + * + * The string is allocated with packet lifetime scope. + * You do not need to [g_]free() this string since it will be automatically + * freed once the next packet is dissected. + */ +char * +proto_construct_match_selected_string(field_info *finfo, epan_dissect_t *edt) +{ + char *filter; + + if (!construct_match_selected_string(finfo, edt, &filter)) + return NULL; + return filter; +} + +/* This function is common code for both proto_tree_add_bitmask() and + * proto_tree_add_bitmask_text() functions. + */ + +/* NOTE: to support code written when proto_tree_add_bitmask() and + * proto_tree_add_bitmask_text took a + * gboolean as its last argument, with FALSE meaning "big-endian" + * and TRUE meaning "little-endian", we treat any non-zero value of + * "encoding" as meaning "little-endian". + */ +static gboolean +proto_item_add_bitmask_tree(proto_item *item, tvbuff_t *tvb, const int offset, + const int len, const gint ett, const int **fields, + const guint encoding, const int flags, + gboolean first) +{ + guint32 value = 0; + guint32 available_bits = 0; + guint32 tmpval; + proto_tree *tree = NULL; + header_field_info *hf; + + switch (len) { + case 1: + value = tvb_get_guint8(tvb, offset); + available_bits = 0xFF; + break; + case 2: + value = encoding ? tvb_get_letohs(tvb, offset) : + tvb_get_ntohs(tvb, offset); + available_bits = 0xFFFF; + break; + case 3: + value = encoding ? tvb_get_letoh24(tvb, offset) : + tvb_get_ntoh24(tvb, offset); + available_bits = 0xFFFFFF; + break; + case 4: + value = encoding ? tvb_get_letohl(tvb, offset) : + tvb_get_ntohl(tvb, offset); + available_bits = 0xFFFFFFFF; + break; + default: + g_assert_not_reached(); + } + + tree = proto_item_add_subtree(item, ett); + while (*fields) { + guint32 present_bits; + hf = proto_registrar_get_nth(**fields); + DISSECTOR_ASSERT(hf->bitmask != 0); + + /* Skip fields that aren't fully present */ + present_bits = available_bits & hf->bitmask; + if (present_bits != hf->bitmask) { + fields++; + continue; + } + + proto_tree_add_item(tree, **fields, tvb, offset, len, encoding); + if (flags & BMT_NO_APPEND) { + fields++; + continue; + } + tmpval = (value & hf->bitmask) >> hf->bitshift; + + switch (hf->type) { + case FT_INT8: + case FT_UINT8: + case FT_INT16: + case FT_UINT16: + case FT_INT24: + case FT_UINT24: + case FT_INT32: + case FT_UINT32: + if (hf->display == BASE_CUSTOM) { + gchar lbl[ITEM_LABEL_LENGTH]; + const custom_fmt_func_t fmtfunc = (const custom_fmt_func_t)hf->strings; + + DISSECTOR_ASSERT(fmtfunc); + fmtfunc(lbl, tmpval); + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, lbl); + first = FALSE; + } + else if (hf->strings) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, hf_try_val_to_str_const(tmpval, hf, "Unknown")); + first = FALSE; + } + else if (!(flags & BMT_NO_INT)) { + char buf[32]; + const char *out; + + if (!first) { + proto_item_append_text(item, ", "); + } + + out = hfinfo_number_value_format(hf, buf, tmpval); + proto_item_append_text(item, "%s: %s", hf->name, out); + first = FALSE; + } + + break; + case FT_BOOLEAN: + if (hf->strings && !(flags & BMT_NO_TFS)) { + /* If we have true/false strings, emit full - otherwise messages + might look weird */ + const struct true_false_string *tfs = + (const struct true_false_string *)hf->strings; + + if (tmpval) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, tfs->true_string); + first = FALSE; + } else if (!(flags & BMT_NO_FALSE)) { + proto_item_append_text(item, "%s%s: %s", first ? "" : ", ", + hf->name, tfs->false_string); + first = FALSE; + } + } else if (hf->bitmask & value) { + /* If the flag is set, show the name */ + proto_item_append_text(item, "%s%s", first ? "" : ", ", hf->name); + first = FALSE; + } + break; + default: + g_assert_not_reached(); + } + + fields++; + } + + return first; +} + +/* This function will dissect a sequence of bytes that describe a + * bitmask. + * hf_hdr is a 8/16/24/32 bit integer that describes the bitmask to be dissected. + * This field will form an expansion under which the individual fields of the + * bitmask is dissected and displayed. + * This field must be of the type FT_[U]INT{8|16|24|32}. + * + * fields is an array of pointers to int that lists all the fields of the + * bitmask. These fields can be either of the type FT_BOOLEAN for flags + * or another integer of the same type/size as hf_hdr with a mask specified. + * This array is terminated by a NULL entry. + * + * FT_BOOLEAN bits that are set to 1 will have the name added to the expansion. + * FT_integer fields that have a value_string attached will have the + * matched string displayed on the expansion line. + */ +proto_item * +proto_tree_add_bitmask(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const int hf_hdr, + const gint ett, const int **fields, + const guint encoding) +{ + proto_item *item = NULL; + header_field_info *hf; + int len; + + hf = proto_registrar_get_nth(hf_hdr); + DISSECTOR_ASSERT(IS_FT_INT(hf->type) || IS_FT_UINT(hf->type)); + len = ftype_length(hf->type); + + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_hdr, tvb, offset, len, encoding); + proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, encoding, + BMT_NO_INT|BMT_NO_TFS, FALSE); + } + + return item; +} + +/* The same as proto_tree_add_bitmask(), but using a caller-supplied length. + * This is intended to support bitmask fields whose lengths can vary, perhaps + * as the underlying standard evolves over time. + * With this API there is the possibility of being called to display more or + * less data than the dissector was coded to support. + * In such cases, it is assumed that bitmasks are extended on the MSb end. + * Thus when presented with "too much" or "too little" data, MSbits will be + * ignored or MSfields sacrificed. + * + * Only fields for which all defined bits are available are displayed. + */ +proto_item * +proto_tree_add_bitmask_len(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const guint len, const int hf_hdr, + const gint ett, const int **fields, + const guint encoding) +{ + proto_item *item = NULL; + header_field_info *hf; + + hf = proto_registrar_get_nth(hf_hdr); + DISSECTOR_ASSERT(IS_FT_INT(hf->type) || IS_FT_UINT(hf->type)); + + if (parent_tree) { + guint decodable_len; + guint decodable_offset; + guint32 decodable_value; + + decodable_offset = offset; + decodable_len = MIN(len, (guint) ftype_length(hf->type)); + + /* If we are ftype_length-limited, + * make sure we decode as many LSBs as possible. + */ + if (encoding == ENC_BIG_ENDIAN) { + decodable_offset += (len - decodable_len); + } + + decodable_value = get_uint_value(parent_tree, tvb, decodable_offset, + decodable_len, encoding); + + /* The root item covers all the bytes even if we can't decode them all */ + item = proto_tree_add_uint(parent_tree, hf_hdr, tvb, offset, len, + decodable_value); + + if (decodable_len < len) { + /* Dissector likely requires updating for new protocol revision */ + expert_add_info_format(NULL, item, PI_UNDECODED, PI_WARN, + "Only least-significant %d of %d bytes decoded", + decodable_len, len); + } + + proto_item_add_bitmask_tree(item, tvb, decodable_offset, decodable_len, + ett, fields, encoding, BMT_NO_INT|BMT_NO_TFS, FALSE); + } + + return item; +} + +/* The same as proto_tree_add_bitmask(), but using an arbitrary text as a top-level item */ +proto_item * +proto_tree_add_bitmask_text(proto_tree *parent_tree, tvbuff_t *tvb, + const guint offset, const guint len, + const char *name, const char *fallback, + const gint ett, const int **fields, + const guint encoding, const int flags) +{ + proto_item *item = NULL; + + if (parent_tree) { + item = proto_tree_add_text(parent_tree, tvb, offset, len, "%s", name ? name : ""); + if (proto_item_add_bitmask_tree(item, tvb, offset, len, ett, fields, encoding, + flags, TRUE) && fallback) { + /* Still at first item - append 'fallback' text if any */ + proto_item_append_text(item, "%s", fallback); + } + } + + return item; +} + +proto_item * +proto_tree_add_bits_item(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + const guint encoding) +{ + header_field_info *hfinfo; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hfinfo); + + return proto_tree_add_bits_ret_val(tree, hf_index, tvb, bit_offset, no_of_bits, NULL, encoding); +} + +/* + * This function will dissect a sequence of bits that does not need to be byte aligned; the bits + * set will be shown in the tree as ..10 10.. and the integer value returned if return_value is set. + * Offset should be given in bits from the start of the tvb. + */ + +static proto_item * +_proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + guint64 *return_value, const guint encoding) +{ + gint offset; + guint length; + guint8 tot_no_bits; + char *bf_str; + char lbl_str[ITEM_LABEL_LENGTH]; + guint64 value = 0; + + proto_item *pi; + header_field_info *hf_field; + + const true_false_string *tfstring; + + /* We can't fake it just yet. We have to fill in the 'return_value' parameter */ + PROTO_REGISTRAR_GET_NTH(hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf("Incompatible use of proto_tree_add_bits_ret_val" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + DISSECTOR_ASSERT(no_of_bits > 0); + + /* Byte align offset */ + offset = bit_offset>>3; + + /* + * Calculate the number of octets used to hold the bits + */ + tot_no_bits = ((bit_offset&0x7) + no_of_bits); + length = tot_no_bits>>3; + /* If we are using part of the next octet, increase length by 1 */ + if (tot_no_bits & 0x07) + length++; + + if (no_of_bits < 65) { + value = tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding); + } else { + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + } + + /* Sign extend for signed types */ + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + if (value & (G_GINT64_CONSTANT(1) << (no_of_bits-1))) + value |= (G_GINT64_CONSTANT(-1) << no_of_bits); + break; + + default: + break; + } + + if (return_value) { + *return_value = value; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + bf_str = decode_bits_in_field(bit_offset, no_of_bits, value); + + switch (hf_field->type) { + case FT_BOOLEAN: + /* Boolean field */ + tfstring = (const true_false_string *) &tfs_true_false; + if (hf_field->strings) + tfstring = (const true_false_string *)hf_field->strings; + return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, (guint32)value, + "%s = %s: %s", + bf_str, hf_field->name, + (guint32)value ? tfstring->true_string : tfstring->false_string); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + pi = proto_tree_add_uint(tree, hf_index, tvb, offset, length, (guint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_int(tree, hf_index, tvb, offset, length, (gint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + case FT_UINT64: + pi = proto_tree_add_uint64(tree, hf_index, tvb, offset, length, value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT64: + pi = proto_tree_add_int64(tree, hf_index, tvb, offset, length, (gint64)value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + proto_item_set_text(pi, "%s = %s", bf_str, lbl_str); + return pi; +} + +proto_item * +proto_tree_add_split_bits_item_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const crumb_spec_t *crumb_spec, + guint64 *return_value) +{ + proto_item *pi; + gint no_of_bits; + gint octet_offset; + guint mask_initial_bit_offset; + guint mask_greatest_bit_offset; + guint octet_length; + guint8 i; + char *bf_str; + char lbl_str[ITEM_LABEL_LENGTH]; + guint64 value; + guint64 composite_bitmask; + guint64 composite_bitmap; + + header_field_info *hf_field; + const true_false_string *tfstring; + + /* We can't fake it just yet. We have to fill in the 'return_value' parameter */ + PROTO_REGISTRAR_GET_NTH(hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf( + "Incompatible use of proto_tree_add_split_bits_item_ret_val" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + mask_initial_bit_offset = bit_offset % 8; + + no_of_bits = 0; + value = 0; + i = 0; + mask_greatest_bit_offset = 0; + composite_bitmask = 0; + composite_bitmap = 0; + + while (crumb_spec[i].crumb_bit_length != 0) { + guint64 crumb_mask, crumb_value; + guint8 crumb_end_bit_offset; + + DISSECTOR_ASSERT(i < 64); + crumb_value = tvb_get_bits64(tvb, + bit_offset + crumb_spec[i].crumb_bit_offset, + crumb_spec[i].crumb_bit_length, + ENC_BIG_ENDIAN); + value += crumb_value; + no_of_bits += crumb_spec[i].crumb_bit_length; + + /* The bitmask is 64 bit, left-aligned, starting at the first bit of the + octet containing the initial offset. + If the mask is beyond 32 bits, then give up on bit map display. + This could be improved in future, probably showing a table + of 32 or 64 bits per row */ + if (mask_greatest_bit_offset < 32) { + crumb_end_bit_offset = mask_initial_bit_offset + + crumb_spec[i].crumb_bit_offset + + crumb_spec[i].crumb_bit_length; + crumb_mask = (G_GUINT64_CONSTANT(1) << crumb_spec[i].crumb_bit_length) - 1; + + if (crumb_end_bit_offset > mask_greatest_bit_offset) { + mask_greatest_bit_offset = crumb_end_bit_offset; + } + composite_bitmask |= (crumb_mask << (64 - crumb_end_bit_offset)); + composite_bitmap |= (crumb_value << (64 - crumb_end_bit_offset)); + } + /* Shift left for the next segment */ + value <<= crumb_spec[++i].crumb_bit_length; + } + + /* Sign extend for signed types */ + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + case FT_INT64: + if (no_of_bits && (value & (G_GINT64_CONSTANT(1) << (no_of_bits-1)))) + value |= (G_GINT64_CONSTANT(-1) << no_of_bits); + break; + default: + break; + } + + if (return_value) { + *return_value = value; + } + + /* Coast clear. Try and fake it */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + /* initialise the format string */ + bf_str = (char *)ep_alloc(256); + bf_str[0] = '\0'; + + octet_offset = bit_offset >> 3; + + /* Round up mask length to nearest octet */ + octet_length = ((mask_greatest_bit_offset + 7) >> 3); + mask_greatest_bit_offset = octet_length << 3; + + /* As noted above, we currently only produce a bitmap if the crumbs span less than 4 octets of the tvb. + It would be a useful enhancement to eliminate this restriction. */ + if (mask_greatest_bit_offset <= 32) { + other_decode_bitfield_value(bf_str, + (guint32)(composite_bitmap >> (64 - mask_greatest_bit_offset)), + (guint32)(composite_bitmask >> (64 - mask_greatest_bit_offset)), + mask_greatest_bit_offset); + } + + switch (hf_field->type) { + case FT_BOOLEAN: /* it is a bit odd to have a boolean encoded as split-bits, but possible, I suppose? */ + /* Boolean field */ + tfstring = (const true_false_string *) &tfs_true_false; + if (hf_field->strings) + tfstring = (const true_false_string *) hf_field->strings; + return proto_tree_add_boolean_format(tree, hf_index, + tvb, octet_offset, octet_length, (guint32)value, + "%s = %s: %s", + bf_str, hf_field->name, + (guint32)value ? tfstring->true_string : tfstring->false_string); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + pi = proto_tree_add_uint(tree, hf_index, tvb, octet_offset, octet_length, (guint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_int(tree, hf_index, tvb, octet_offset, octet_length, (gint32)value); + fill_label_number(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + case FT_UINT64: + pi = proto_tree_add_uint64(tree, hf_index, tvb, octet_offset, octet_length, value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, FALSE); + break; + + case FT_INT64: + pi = proto_tree_add_int64(tree, hf_index, tvb, octet_offset, octet_length, (gint64)value); + fill_label_number64(PITEM_FINFO(pi), lbl_str, TRUE); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + proto_item_set_text(pi, "%s = %s", bf_str, lbl_str); + return pi; +} + +void +proto_tree_add_split_bits_crumb(proto_tree *tree, const int hf_index, tvbuff_t *tvb, const guint bit_offset, + const crumb_spec_t *crumb_spec, guint16 crumb_index) +{ + header_field_info *hf_info; + + PROTO_REGISTRAR_GET_NTH(hf_index, hf_info); + proto_tree_add_text(tree, tvb, + bit_offset >> 3, + ((bit_offset + crumb_spec[crumb_index].crumb_bit_length - 1) >> 3) - (bit_offset >> 3) + 1, + "%s crumb %d of %s (decoded above)", + decode_bits_in_field(bit_offset, crumb_spec[crumb_index].crumb_bit_length, + tvb_get_bits(tvb, + bit_offset, + crumb_spec[crumb_index].crumb_bit_length, + ENC_BIG_ENDIAN)), + crumb_index, + hf_info->name); +} + +proto_item * +proto_tree_add_bits_ret_val(proto_tree *tree, const int hf_index, tvbuff_t *tvb, + const guint bit_offset, const gint no_of_bits, + guint64 *return_value, const guint encoding) +{ + proto_item *item; + + if ((item = _proto_tree_add_bits_ret_val(tree, hf_index, tvb, + bit_offset, no_of_bits, + return_value, encoding))) { + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset)); + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits)); + } + return item; +} + +static proto_item * +_proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, void *value_ptr, + gchar *value_str) +{ + gint offset; + guint length; + guint8 tot_no_bits; + char *str; + guint64 value = 0; + header_field_info *hf_field; + + /* We do not have to return a value, try to fake it as soon as possible */ + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + if (hf_field->bitmask != 0) { + REPORT_DISSECTOR_BUG(ep_strdup_printf( + "Incompatible use of proto_tree_add_bits_format_value" + " with field '%s' (%s) with bitmask != 0", + hf_field->abbrev, hf_field->name)); + } + + DISSECTOR_ASSERT(no_of_bits > 0); + + /* Byte align offset */ + offset = bit_offset>>3; + + /* + * Calculate the number of octets used to hold the bits + */ + tot_no_bits = ((bit_offset&0x7) + no_of_bits); + length = tot_no_bits>>3; + /* If we are using part of the next octet, increase length by 1 */ + if (tot_no_bits & 0x07) + length++; + + if (no_of_bits < 65) { + value = tvb_get_bits64(tvb, bit_offset, no_of_bits, ENC_BIG_ENDIAN); + } else { + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + } + + str = decode_bits_in_field(bit_offset, no_of_bits, value); + + strcat(str, " = "); + strcat(str, hf_field->name); + + /* + * This function does not receive an actual value but a dimensionless pointer to that value. + * For this reason, the type of the header field is examined in order to determine + * what kind of value we should read from this address. + * The caller of this function must make sure that for the specific header field type the address of + * a compatible value is provided. + */ + switch (hf_field->type) { + case FT_BOOLEAN: + return proto_tree_add_boolean_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + return proto_tree_add_uint_format(tree, hf_index, tvb, offset, length, *(guint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_UINT64: + return proto_tree_add_uint64_format(tree, hf_index, tvb, offset, length, *(guint64 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + return proto_tree_add_int_format(tree, hf_index, tvb, offset, length, *(gint32 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_INT64: + return proto_tree_add_int64_format(tree, hf_index, tvb, offset, length, *(gint64 *)value_ptr, + "%s: %s", str, value_str); + break; + + case FT_FLOAT: + return proto_tree_add_float_format(tree, hf_index, tvb, offset, length, *(float *)value_ptr, + "%s: %s", str, value_str); + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } +} + +static proto_item * +proto_tree_add_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, void *value_ptr, + gchar *value_str) +{ + proto_item *item; + + if ((item = _proto_tree_add_bits_format_value(tree, hf_index, + tvb, bit_offset, no_of_bits, + value_ptr, value_str))) { + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_OFFSET(bit_offset)); + FI_SET_FLAG(PNODE_FINFO(item), FI_BITS_SIZE(no_of_bits)); + } + return item; +} + +#define CREATE_VALUE_STRING(dst,format,ap) \ + va_start(ap, format); \ + dst = ep_strdup_vprintf(format, ap); \ + va_end(ap); + +proto_item * +proto_tree_add_uint_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, guint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + switch (hf_field->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_float_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, float value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + DISSECTOR_ASSERT(hf_field->type == FT_FLOAT); + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_int_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, gint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + switch (hf_field->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + break; + + default: + DISSECTOR_ASSERT_NOT_REACHED(); + return NULL; + break; + } + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +proto_item * +proto_tree_add_boolean_bits_format_value(proto_tree *tree, const int hf_index, + tvbuff_t *tvb, const guint bit_offset, + const gint no_of_bits, guint32 value, + const char *format, ...) +{ + va_list ap; + gchar *dst; + header_field_info *hf_field; + + TRY_TO_FAKE_THIS_ITEM(tree, hf_index, hf_field); + + DISSECTOR_ASSERT(hf_field->type == FT_BOOLEAN); + + CREATE_VALUE_STRING(dst, format, ap); + + return proto_tree_add_bits_format_value(tree, hf_index, tvb, bit_offset, no_of_bits, &value, dst); +} + +guchar +proto_check_field_name(const gchar *field_name) +{ + return wrs_check_charset(fld_abbrev_chars, field_name); +} + +gboolean +tree_expanded(int tree_type) +{ + g_assert(tree_type >= 0 && tree_type < num_tree_types); + return tree_is_expanded[tree_type >> 5] & (1 << (tree_type & 31)); +} + +void +tree_expanded_set(int tree_type, gboolean value) +{ + g_assert(tree_type >= 0 && tree_type < num_tree_types); + + if (value) + tree_is_expanded[tree_type >> 5] |= (1 << (tree_type & 31)); + else + tree_is_expanded[tree_type >> 5] &= ~(1 << (tree_type & 31)); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ diff --git a/src/main/resource/testFiles/wireshark/revFiles/96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c b/src/main/resource/testFiles/wireshark/revFiles/96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c new file mode 100644 index 0000000..a32e81a --- /dev/null +++ b/src/main/resource/testFiles/wireshark/revFiles/96ba09_755c08_epan#dissectors#packet-gsm_a_rr.c @@ -0,0 +1,12477 @@ +/* packet-gsm_a_rr.c + * Routines for GSM A Interface (actually A-bis really) RR dissection - A.K.A. GSM layer 3 Radio Resource Protocol + * + * Copyright 2003, Michael Lum + * In association with Telos Technology Inc. + * + * Added Dissection of Radio Resource Management Information Elements + * and other enhancements and fixes. + * Copyright 2005 - 2006, Anders Broman [AT] ericsson.com + * + * Title 3GPP Other + * + * Reference [3] + * Mobile radio interface Layer 3 specification; + * Core network protocols; + * Stage 3 + * (3GPP TS 24.008 version 4.7.0 Release 4) + * (ETSI TS 124 008 V6.8.0 (2005-03)) + * + * Reference [4] + * Mobile radio interface layer 3 specification; + * Radio Resource Control Protocol + * (GSM 04.18 version 8.4.1 Release 1999) + * (3GPP TS 04.18 version 8.26.0 Release 1999) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "packet-bssap.h" +#include "packet-sccp.h" +#include "packet-ber.h" +#include "packet-q931.h" +#include "packet-gsm_a_common.h" +#include "packet-e212.h" +#include "packet-ppp.h" + +static dissector_handle_t rrc_irat_ho_info_handle; +static dissector_handle_t rrc_irat_ho_to_utran_cmd_handle; + +#define PADDING_BYTE 0x2B + +/* PROTOTYPES/FORWARDS */ + +const value_string gsm_a_dtap_msg_rr_strings[] = { + { 0x3c, "Reserved" }, + { 0x3b, "Additional Assignment" }, + { 0x3f, "Immediate Assignment" }, + { 0x39, "Immediate Assignment Extended" }, + { 0x3a, "Immediate Assignment Reject" }, + + { 0x48, "DTM Assignment Failure" }, + { 0x49, "DTM Reject" }, + { 0x4a, "DTM Request" }, + { 0x4b, "Main DCCH Assignment Command" }, + { 0x4c, "Packet Assignment Command" }, + + { 0x35, "Ciphering Mode Command" }, + { 0x32, "Ciphering Mode Complete" }, + + { 0x30, "Configuration Change Command" }, + { 0x31, "Configuration Change Ack." }, + { 0x33, "Configuration Change Reject" }, + + { 0x2e, "Assignment Command" }, + { 0x29, "Assignment Complete" }, + { 0x2f, "Assignment Failure" }, + { 0x2b, "Handover Command" }, + { 0x2c, "Handover Complete" }, + { 0x28, "Handover Failure" }, + { 0x2d, "Physical Information" }, + { 0x4d, "DTM Assignment Command" }, + + { 0x08, "RR-cell Change Order" }, + { 0x23, "PDCH Assignment Command" }, + + { 0x0d, "Channel Release" }, + { 0x0a, "Partial Release" }, + { 0x0f, "Partial Release Complete" }, + + { 0x21, "Paging Request Type 1" }, + { 0x22, "Paging Request Type 2" }, + { 0x24, "Paging Request Type 3" }, + { 0x27, "Paging Response" }, + { 0x20, "Notification/NCH" }, + { 0x25, "Reserved" }, + { 0x26, "Notification/Response" }, + + { 0x0b, "Reserved" }, + +#if 0 +/* ETSI TS 101 503 V8.5.0 Seems to give Other def for this Messages??? */ + { 0xc0, "Utran Classmark Change" }, CONFLICTS WITH Handover To UTRAN Command + { 0xc1, "UE RAB Preconfiguration" }, + { 0xc2, "cdma2000 Classmark Change" }, +#endif + + /* ETSI TS 101 503 V8.5.0 */ + { 0x60, "Utran Classmark Change" }, + { 0x62, "cdma2000 Classmark Change" }, + { 0x63, "Inter System to UTRAN Handover Command" }, + { 0x64, "Inter System to cdma2000 Handover Command" }, + { 0x18, "System Information Type 8" }, + { 0x19, "System Information Type 1" }, + { 0x1a, "System Information Type 2" }, + { 0x1b, "System Information Type 3" }, + { 0x1c, "System Information Type 4" }, + { 0x1d, "System Information Type 5" }, + { 0x1e, "System Information Type 6" }, + { 0x1f, "System Information Type 7" }, + + { 0x02, "System Information Type 2bis" }, + { 0x03, "System Information Type 2ter" }, + { 0x07, "System Information Type 2quater" }, + { 0x05, "System Information Type 5bis" }, + { 0x06, "System Information Type 5ter" }, + { 0x04, "System Information Type 9" }, + { 0x00, "System Information Type 13" }, + + { 0x3d, "System Information Type 16" }, + { 0x3e, "System Information Type 17" }, + + { 0x40, "System Information Type 18" }, + { 0x41, "System Information Type 19" }, + { 0x42, "System Information Type 20" }, + + { 0x10, "Channel Mode Modify" }, + { 0x12, "RR Status" }, + { 0x17, "Channel Mode Modify Acknowledge" }, + { 0x14, "Frequency Redefinition" }, + { 0x15, "Measurement Report" }, + { 0x16, "Classmark Change" }, + { 0x13, "Classmark Enquiry" }, + { 0x36, "Extended Measurement Report" }, + { 0x37, "Extended Measurement Order" }, + { 0x34, "GPRS Suspension Request" }, + + { 0x09, "VGCS Uplink Grant" }, + { 0x0e, "Uplink Release" }, + { 0x0c, "Reserved" }, + { 0x2a, "Uplink Busy" }, + { 0x11, "Talker Indication" }, + + { 0xc0, "UTRAN Classmark Change/Handover To UTRAN Command" }, /* spec conflict */ + + { 0x38, "Application Information" }, + + { 0, NULL } +}; + +const value_string gsm_rr_elem_strings[] = { + /* Radio Resource Management Information Elements 10.5.2, most are from 10.5.1 */ + { 0x00, "BA Range" }, /* [3] 10.5.2.1a BA Range */ + { 0x00, "Cell Channel Description" }, /* [3] 10.5.2.1b */ + { 0x00, "BA List Pref" }, /* [3] 10.5.2.1c BA List Pref */ + { 0x00, "UTRAN Frequency List" }, /* [3] 10.5.2.1d UTRAN Frequency List */ + { 0x00, "Cell Selection Indicator after Release of all TCH and SDCCH" }, /* [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH IE */ + { 0x00, "Cell Description" }, /* 10.5.2.2 */ + { 0x00, "Cell Options (BCCH)" }, /* [3] 10.5.2.3 Cell Options (BCCH) */ + { 0x00, "Cell Options (SACCH)" }, /* [3] 10.5.2.3a Cell Options (SACCH) */ + { 0x00, "Cell Selection Parameters" }, /* [3] 10.5.2.4 Cell Selection Parameters */ +/* [3] 10.5.2.4a (void) */ + { 0x00, "Channel Description" }, /* 10.5.2.5 */ + { 0x00, "Channel Description 2" }, /* 10.5.2.5a */ + { 0x00, "Channel Description 3" }, /* 10.5.2.5c */ + { 0x00, "Channel Mode" }, /* [3] 10.5.2.6 */ + { 0x00, "Channel Mode 2" }, /* [3] 10.5.2.7 */ + { 0x00, "UTRAN Classmark" }, /* [3] 10.5.2.7a */ +/* [3] 10.5.2.7b (void) */ + { 0x00, "Classmark Enquiry Mask" }, /* [3] 10.5.2.7c */ +/* [3] 10.5.2.7d GERAN Iu Mode Classmark information element */ + { 0x00, "Channel Needed"}, /* [3] 10.5.2.8 */ + /* [3] 10.5.2.8a (void) */ + /* [3] 10.5.2.8b Channel Request Description 2 */ + /* Pos 20 */ + { 0x00, "Cipher Mode Setting" }, /* [3] 10.5.2.9 */ + { 0x00, "Cipher Mode Response" }, /* [3] 10.5.2.10 */ + { 0x00, "Control Channel Description" }, /* [3] 10.5.2.11 Control Channel Description */ +/* [3] 10.5.2.11a DTM Information Details */ + { 0x00, "Dynamic ARFCN Mapping" }, /* [3] 10.5.2.11b */ + { 0x00, "Frequency Channel Sequence" }, /* [3] 10.5.2.12 */ + { 0x00, "Frequency List" }, /* 10.5.2.13 */ + { 0x00, "Frequency Short List" }, /* 10.5.2.14 */ + { 0x00, "Frequency Short List2" }, /* 10.5.2.14a */ +/* [3] 10.5.2.14b Group Channel Description */ + { 0x00, "GPRS Resumption" }, /* [3] 10.5.2.14c GPRS Resumption */ + { 0x00, "GPRS Broadcast Information" }, /* [3] 10.5.2.14d GPRS broadcast information */ +/* [3] 10.5.2.14e Enhanced DTM CS Release Indication */ + { 0x00, "Handover Reference" }, /* 10.5.2.15 */ + { 0x00, "IA Rest Octets" }, /* [3] 10.5.2.16 */ + { 0x00, "IAR Rest Octets" }, /* [3] 10.5.2.17 IAR Rest Octets */ + { 0x00, "IAX Rest Octets" }, /* [3] 10.5.2.18 IAX Rest Octets */ + { 0x00, "L2 Pseudo Length" }, /* [3] 10.5.2.19 */ + { 0x00, "Measurement Results" }, /* [3] 10.5.2.20 Measurement Results */ +/* + * [3] 10.5.2.20a GPRS Measurement Results + */ + { 0x00, "Mobile Allocation" }, /* [3] 10.5.2.21 */ + { 0x00, "Mobile Time Difference" }, /* [3] 10.5.2.21a */ + { 0x00, "MultiRate configuration" }, /* [3] 10.5.2.21aa */ + /* Pos 30 */ + { 0x00, "Multislot Allocation" }, /* [3] 10.5.2.21b */ + /* + * [3] 10.5.2.21c NC mode + */ + { 0x00, "Neighbour Cell Description" }, /* [3] 10.5.2.22 Neighbour Cell Description */ + { 0x00, "Neighbour Cell Description 2" }, /* [3] 10.5.2.22a Neighbour Cell Description 2 */ +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets */ + { 0x00, "P1 Rest Octets" }, /* [3] 10.5.2.23 P1 Rest Octets */ + { 0x00, "P2 Rest Octets" }, /* [3] 10.5.2.24 P2 Rest Octets */ + { 0x00, "P3 Rest Octets" }, /* [3] 10.5.2.25 P3 Rest Octets */ + { 0x00, "Packet Channel Description" }, /* [3] 10.5.2.25a */ + { 0x00, "Dedicated mode or TBF" }, /* [3] 10.5.2.25b */ + /* [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment */ + { 0x00, "Page Mode" }, /* [3] 10.5.2.26 */ +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ + { 0x00, "NCC Permitted" }, /* [3] 10.5.2.27 NCC Permitted */ + { 0x00, "Power Command" }, /* 10.5.2.28 */ + { 0x00, "Power Command and access type" }, /* 10.5.2.28a */ + { 0x00, "RACH Control Parameters" }, /* [3] 10.5.2.29 RACH Control Parameters */ + { 0x00, "Request Reference" }, /* [3] 10.5.2.30 Request Reference */ + { 0x00, "RR Cause" }, /* 10.5.2.31 */ + { 0x00, "Synchronization Indication" }, /* 10.5.2.39 */ + { 0x00, "SI 1 Rest Octets" }, /* [3] 10.5.2.32 */ +/* [3] 10.5.2.33 SI 2bis Rest Octets */ + { 0x00, "SI 2ter Rest Octets" }, /* [3] 10.5.2.33a */ + { 0x00, "SI 2quater Rest Octets" }, /* [3] 10.5.2.33b */ + { 0x00, "SI 3 Rest Octets" }, /* [3] 10.5.2.34 */ + { 0x00, "SI 4 Rest Octets" }, /* [3] 10.5.2.35 */ + { 0x00, "SI 6 Rest Octets" }, /* [3] 10.5.2.35a */ +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + { 0x00, "SI 13 Rest Octets" }, /* [3] 10.5.2.37b */ +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets */ + { 0x00, "Starting Time" }, /* [3] 10.5.2.38 Starting Time */ + { 0x00, "Timing Advance" }, /* [3] 10.5.2.40 Timing Advance */ + { 0x00, "Time Difference" }, /* [3] 10.5.2.41 Time Difference */ + { 0x00, "TLLI" }, /* [3] 10.5.2.41a TLLI */ + { 0x00, "TMSI/P-TMSI" }, /* [3] 10.5.2.42 TMSI/P-TMSI */ + { 0x00, "VGCS target mode Indication" }, /* [3] 10.5.2.42a */ + /* Pos 40 */ + { 0x00, "VGCS Ciphering Parameters" }, /* [3] 10.5.2.42b */ + { 0x00, "Wait Indication" }, /* [3] 10.5.2.43 Wait Indication */ +/* [3] 10.5.2.44 SI10 rest octets $(ASCI)$ */ + { 0x00, "Extended Measurement Results" }, /* [3] 10.5.2.45 Extended Measurement Results */ + { 0x00, "Extended Measurement Frequency List" }, /* [3] 10.5.2.46 Extended Measurement Frequency List */ + { 0x00, "Suspension Cause" }, /* [3] 10.5.2.47 */ + { 0x00, "APDU ID" }, /* [3] 10.5.2.48 APDU ID */ + { 0x00, "APDU Flags" }, /* [3] 10.5.2.49 APDU Flags */ + { 0x00, "APDU Data" }, /* [3] 10.5.2.50 APDU Data */ + { 0x00, "Handover to UTRAN Command" }, /* [3] 10.5.2.51 Handover To UTRAN Command */ +/* [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell */ + { 0x00, "Service Support" }, /* [3] 10.5.2.57 */ + /* 10.5.2.58 MBMS p-t-m Channel Description */ + { 0x00, "Dedicated Service Information" }, /* [3] 10.5.2.59 */ +/* + * 10.5.2.60 MPRACH Description + * 10.5.2.61 Restriction Timer + * 10.5.2.62 MBMS Session Identity + * 10.5.2.63 Reduced group or broadcast call reference + * 10.5.2.64 Talker Priority status + * 10.5.2.65 Talker Identity + * 10.5.2.66 Token + * 10.5.2.67 PS Cause + * 10.5.2.68 VGCS AMR Configuration + */ + { 0x00, "Carrier Indication" }, /* 10.5.2.69 Carrier Indication */ + { 0, NULL } +}; + +const value_string gsm_rr_rest_octets_elem_strings[] = { + /* RR Rest Octets information elements */ + { 0, "UTRAN FDD Description" }, + { 0, "UTRAN TDD Description" }, + { 0, "3G Measurement Parameters Description" }, + { 0, "3G Additional Measurement Parameters Description" }, + { 0, "Measurement Parameters Description" }, + { 0, "GPRS Real Time Difference Description" }, + { 0, "GPRS BSIC Description" }, + { 0, "GPRS Report Priority Description" }, + { 0, "GPRS Measurement Parameters Description" }, + { 0, "NC Measurement Parameters" }, + { 0, "SI2q Extension Information" }, + { 0, "CCN Support Description" }, + { 0, "3G Neighbour Cell Description" }, + { 0, "FDD Cell Information Field" }, + { 0, "TDD Cell Information Field" }, + { 0, "GPRS 3G Measurement Parameters Description" }, + { 0, "3G Additional Measurement Parameters Description 2" }, + { 0, "Priority and E-UTRAN Parameters Description" }, + { 0, "Serving Cell Priority Parameters Description" }, + { 0, "3G Priority Parameters Description" }, + { 0, "UTRAN Priority Parameters" }, + { 0, "E-UTRAN Parameters Description" }, + { 0, "E-UTRAN Neighbour Cells" }, + { 0, "E-UTRAN Not Allowed Cells" }, + { 0, "E-UTRAN PCID to TA mapping" }, + { 0, "3G CSG Description" }, + { 0, "E-UTRAN CSG Description" }, + { 0, "Optional Selection Parameters" }, + { 0, "GPRS Indicator" }, + { 0, "SI4 Rest Octets_O" }, + { 0, "SI4 Rest Octets_S" }, + { 0, "LSA Parameters" }, + { 0, "LSA ID Information" }, + { 0, "PCH and NCH Info" }, + { 0, "VBS/VGCS Options" }, + { 0, "GPRS Mobile Allocation" }, + { 0, "GPRS Cell Options" }, + { 0, "GPRS Cell Options Extension Information" }, + { 0, "GPRS Power Control Parameters" }, + { 0, "PBCCH Description" }, + { 0, "GSM Description" }, + { 0, "Real Time Difference Description" }, + { 0, "BSIC Description" }, + { 0, "Report Priority Description" }, + { 0, "CDMA2000 Description" }, + { 0, "Serving cell data" }, + { 0, "Repeated Invalid BSIC Information" }, + { 0, "Bitmap Type Reporting" }, + { 0, "3G Supplementary Parameters Description" }, + { 0, "UTRAN Measurement Control Parameters" }, + { 0, "EGPRS Packet Uplink Assignment" }, + { 0, "Multiple Blocks Packet Downlink Assignment" }, + { 0, "Temporary Mobile Group Identity (TMGI)" }, + { 0, "Packet Timing Advance" }, + { 0, "Packet Uplink Assignment" }, + { 0, "Packet Downlink Assignment" }, + { 0, "Second Part Packet Assignment" }, + { 0, "REPORTING QUANTITY" }, + { 0, "E-UTRAN Measurement Report" }, + { 0, NULL } +}; + + +/* RR cause value (octet 2) TS 44.018 6.11.0*/ +static const value_string gsm_a_rr_RR_cause_vals[] = { + { 0, "Normal event"}, + { 1, "Abnormal release, unspecified"}, + { 2, "Abnormal release, channel unacceptable"}, + { 3, "Abnormal release, timer expired"}, + { 4, "Abnormal release, no activity on the radio path"}, + { 5, "Preemptive release"}, + { 6, "UTRAN configuration unknown"}, + { 8, "Handover impossible, timing advance out of range"}, + { 9, "Channel mode unacceptable"}, + { 10, "Frequency not implemented"}, + { 13, "Originator or talker leaving group call area"}, + { 12, "Lower layer failure"}, + { 0x41, "Call already cleared"}, + { 0x5f, "Semantically incorrect message"}, + { 0x60, "Invalid mandatory information"}, + { 0x61, "Message type non-existent or not implemented"}, + { 0x62, "Message type not compatible with protocol state"}, + { 0x64, "Conditional IE error"}, + { 0x65, "No cell allocation available"}, + { 0x6f, "Protocol error unspecified"}, + { 0, NULL } +}; + +static const value_string gsm_a_algorithm_identifier_vals[] = { + { 0, "Cipher with algorithm A5/1"}, + { 1, "Cipher with algorithm A5/2"}, + { 2, "Cipher with algorithm A5/3"}, + { 3, "Cipher with algorithm A5/4"}, + { 4, "Cipher with algorithm A5/5"}, + { 5, "Cipher with algorithm A5/6"}, + { 6, "Cipher with algorithm A5/7"}, + { 7, "Reserved"}, + { 0, NULL } +}; + + +#define DTAP_PD_MASK 0x0f +#define DTAP_SKIP_MASK 0xf0 +#define DTAP_TI_MASK DTAP_SKIP_MASK +#define DTAP_TIE_PRES_MASK 0x07 /* after TI shifted to right */ +#define DTAP_TIE_MASK 0x7f + +#define DTAP_RR_IEI_MASK 0xff + +/* Initialize the protocol and registered fields */ +static int proto_a_ccch = -1; +static int proto_a_sacch = -1; + +static int hf_gsm_a_dtap_msg_rr_type = -1; +int hf_gsm_a_rr_elem_id = -1; + +static int hf_gsm_a_rr_short_pd_msg_type = -1; +static int hf_gsm_a_rr_short_pd = -1; +static int hf_gsm_a_rr_short_l2_header = -1; + + +static int hf_gsm_a_bcc = -1; +static int hf_gsm_a_ncc = -1; +static int hf_gsm_a_bcch_arfcn = -1; +static int hf_gsm_a_rr_range_nb = -1; +static int hf_gsm_a_rr_range_lower = -1; +static int hf_gsm_a_rr_range_higher = -1; +static int hf_gsm_a_rr_ba_list_pref_length = -1; +static int hf_gsm_a_rr_ba_freq = -1; +static int hf_gsm_a_rr_utran_freq_list_length = -1; +static int hf_gsm_a_rr_ho_ref_val = -1; +static int hf_gsm_a_rr_L2_pseudo_len = -1; +static int hf_gsm_a_rr_ba_used = -1; +static int hf_gsm_a_rr_dtx_used = -1; +static int hf_gsm_a_rr_3g_ba_used = -1; +static int hf_gsm_a_rr_meas_valid = -1; +static int hf_gsm_a_rr_rxlev_full_serv_cell = -1; +static int hf_gsm_a_rr_rxlev_sub_serv_cell = -1; +static int hf_gsm_a_rr_rxqual_full_serv_cell = -1; +static int hf_gsm_a_rr_rxqual_sub_serv_cell = -1; +static int hf_gsm_a_rr_no_ncell_m = -1; +static int hf_gsm_a_rr_rxlev_ncell = -1; +static int hf_gsm_a_rr_bcch_freq_ncell = -1; +static int hf_gsm_a_rr_bsic_ncell = -1; +static int hf_gsm_a_rr_mobile_time_difference = -1; +static int hf_gsm_a_rr_pow_cmd_atc = -1; +static int hf_gsm_a_rr_pow_cmd_epc = -1; +static int hf_gsm_a_rr_page_mode = -1; +static int hf_gsm_a_rr_dedicated_mode_or_tbf = -1; +static int hf_gsm_a_rr_pow_cmd_fpcepc = -1; +static int hf_gsm_a_rr_pow_cmd_powlev = -1; +static int hf_gsm_a_rr_sync_ind_nci = -1; +static int hf_gsm_a_rr_sync_ind_rot = -1; +static int hf_gsm_a_rr_sync_ind_si = -1; +static int hf_gsm_a_rr_format_id = -1; +static int hf_gsm_a_rr_format_id2 = -1; +static int hf_gsm_a_rr_channel_mode = -1; +static int hf_gsm_a_rr_channel_mode2 = -1; +static int hf_gsm_a_rr_sc = -1; +static int hf_gsm_a_algorithm_id = -1; +static int hf_gsm_a_rr_cr = -1; +static int hf_gsm_a_rr_multirate_speech_ver = -1; +static int hf_gsm_a_rr_NCSB = -1; +static int hf_gsm_a_rr_ICMI = -1; +static int hf_gsm_a_rr_start_mode = -1; +static int hf_gsm_a_rr_timing_adv = -1; +static int hf_gsm_a_rr_time_diff = -1; +static int hf_gsm_a_rr_tlli = -1; +static int hf_gsm_a_rr_tmsi_ptmsi = -1; +static int hf_gsm_a_rr_target_mode = -1; +static int hf_gsm_a_rr_wait_indication = -1; +static int hf_gsm_a_rr_seq_code = -1; +static int hf_gsm_a_rr_group_cipher_key_number = -1; +static int hf_gsm_a_rr_MBMS_multicast = -1; +static int hf_gsm_a_rr_MBMS_broadcast = -1; +static int hf_gsm_a_rr_last_segment = -1; +static int hf_gsm_a_rr_carrier_ind = -1; +static int hf_gsm_a_rr_ra = -1; +static int hf_gsm_a_rr_T1prim = -1; +static int hf_gsm_a_rr_T3 = -1; +static int hf_gsm_a_rr_T2 = -1; +static int hf_gsm_a_rr_rfn = -1; +static int hf_gsm_a_rr_RR_cause = -1; +static int hf_gsm_a_rr_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_utran_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_cdma200_cm_cng_msg_req = -1; +static int hf_gsm_a_rr_geran_iu_cm_cng_msg_req = -1; +int hf_gsm_a_rr_chnl_needed_ch1 = -1; +static int hf_gsm_a_rr_chnl_needed_ch2 = -1; +static int hf_gsm_a_rr_chnl_needed_ch3 = -1; +static int hf_gsm_a_rr_chnl_needed_ch4 = -1; +static int hf_gsm_a_rr_suspension_cause = -1; +static int hf_gsm_a_rr_apdu_id = -1; +static int hf_gsm_a_rr_apdu_flags = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2 = -1; +static int hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1 = -1; +static int hf_gsm_a_rr_amr_threshold = -1; +static int hf_gsm_a_rr_amr_hysteresis = -1; +static int hf_gsm_a_rr_pwrc = -1; +static int hf_gsm_a_rr_dtx_bcch = -1; +static int hf_gsm_a_rr_dtx_sacch = -1; +static int hf_gsm_a_rr_radio_link_timeout = -1; +static int hf_gsm_a_rr_cell_reselect_hyst = -1; +static int hf_gsm_a_rr_ms_txpwr_max_cch = -1; +static int hf_gsm_a_rr_acs = -1; +static int hf_gsm_a_rr_neci = -1; +static int hf_gsm_a_rr_rxlev_access_min = -1; +static int hf_gsm_a_rr_mscr = -1; +static int hf_gsm_a_rr_att = -1; +static int hf_gsm_a_rr_ccch_conf = -1; +static int hf_gsm_a_rr_cbq3 = -1; +static int hf_gsm_a_rr_bs_pa_mfrms = -1; +static int hf_gsm_a_rr_bs_ag_blks_res = -1; +static int hf_gsm_a_rr_t3212 = -1; +static int hf_gsm_a_rr_dyn_arfcn_length = -1; +static int hf_gsm_a_rr_gsm_band = -1; +static int hf_gsm_a_rr_arfcn_first = -1; +static int hf_gsm_a_rr_band_offset = -1; +static int hf_gsm_a_rr_arfcn_range = -1; +static int hf_gsm_a_rr_lowest_arfcn = -1; +static int hf_gsm_a_rr_inc_skip_arfcn = -1; +static int hf_gsm_a_rr_gprs_resumption_ack = -1; +static int hf_gsm_a_rr_ext_ind = -1; +static int hf_gsm_a_rr_ba_ind = -1; +static int hf_gsm_a_rr_multiband_reporting = -1; +static int hf_gsm_a_rr_ncc_permitted = -1; +static int hf_gsm_a_rr_max_retrans = -1; +static int hf_gsm_a_rr_tx_integer = -1; +static int hf_gsm_a_rr_cell_barr_access = -1; +static int hf_gsm_a_rr_re = -1; +static int hf_gsm_a_rr_acc = -1; +static int hf_gsm_a_rr_nch_position = -1; +static int hf_gsm_a_rr_si2ter_mp_change_mark = -1; +static int hf_gsm_a_rr_si2ter_3g_change_mark = -1; +static int hf_gsm_a_rr_si2ter_index = -1; +static int hf_gsm_a_rr_si2ter_count = -1; +static int hf_gsm_a_rr_fdd_uarfcn = -1; +static int hf_gsm_a_rr_bandwidth_fdd = -1; +static int hf_gsm_a_rr_tdd_uarfcn = -1; +static int hf_gsm_a_rr_bandwidth_tdd = -1; +static int hf_gsm_a_rr_arfcn = -1; +static int hf_gsm_a_rr_bsic = -1; +static int hf_gsm_a_rr_qsearch_i = -1; +static int hf_gsm_a_rr_fdd_qoffset = -1; +static int hf_gsm_a_rr_fdd_qmin = -1; +static int hf_gsm_a_rr_tdd_qoffset = -1; +static int hf_gsm_a_rr_fdd_qmin_offset = -1; +static int hf_gsm_a_rr_fdd_rscpmin = -1; +static int hf_gsm_a_rr_3g_ba_ind = -1; +static int hf_gsm_a_rr_mp_change_mark = -1; +static int hf_gsm_a_rr_si2quater_index = -1; +static int hf_gsm_a_rr_si2quater_count = -1; +static int hf_gsm_a_rr_gsm_report_type = -1; +static int hf_gsm_a_rr_serving_band_reporting = -1; +static int hf_gsm_a_rr_frequency_scrolling = -1; +static int hf_gsm_a_rr_rep_priority = -1; +static int hf_gsm_a_rr_report_type = -1; +static int hf_gsm_a_rr_reporting_rate = -1; +static int hf_gsm_a_rr_invalid_bsic_reporting = -1; +static int hf_gsm_a_rr_scale_ord = -1; +static int hf_gsm_a_rr_900_reporting_offset = -1; +static int hf_gsm_a_rr_900_reporting_threshold = -1; +static int hf_gsm_a_rr_1800_reporting_offset = -1; +static int hf_gsm_a_rr_1800_reporting_threshold = -1; +static int hf_gsm_a_rr_400_reporting_offset = -1; +static int hf_gsm_a_rr_400_reporting_threshold = -1; +static int hf_gsm_a_rr_1900_reporting_offset = -1; +static int hf_gsm_a_rr_1900_reporting_threshold = -1; +static int hf_gsm_a_rr_850_reporting_offset = -1; +static int hf_gsm_a_rr_850_reporting_threshold = -1; +static int hf_gsm_a_rr_network_control_order = -1; +static int hf_gsm_a_rr_nc_non_drx_period = -1; +static int hf_gsm_a_rr_nc_reporting_period_i = -1; +static int hf_gsm_a_rr_nc_reporting_period_t = -1; +static int hf_gsm_a_rr_index_start_3g = -1; +static int hf_gsm_a_rr_absolute_index_start_emr = -1; +static int hf_gsm_a_rr_qsearch_c_initial = -1; +static int hf_gsm_a_rr_fdd_rep_quant = -1; +static int hf_gsm_a_rr_fdd_multirat_reporting = -1; +static int hf_gsm_a_rr_tdd_multirat_reporting = -1; +static int hf_gsm_a_rr_qsearch_p = -1; +static int hf_gsm_a_rr_3g_search_prio = -1; +static int hf_gsm_a_rr_fdd_reporting_offset = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_rscp = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_ecn0 = -1; +static int hf_gsm_a_rr_tdd_reporting_offset = -1; +static int hf_gsm_a_rr_tdd_reporting_threshold_rscp = -1; +static int hf_gsm_a_rr_tdd_reporting_threshold_ecn0 = -1; +static int hf_gsm_a_rr_fdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_3g_ccn_active = -1; +static int hf_gsm_a_rr_700_reporting_offset = -1; +static int hf_gsm_a_rr_700_reporting_threshold = -1; +static int hf_gsm_a_rr_810_reporting_offset = -1; +static int hf_gsm_a_rr_810_reporting_threshold = -1; +static int hf_gsm_a_rr_cbq = -1; +static int hf_gsm_a_rr_cell_reselect_offset = -1; +static int hf_gsm_a_rr_temporary_offset = -1; +static int hf_gsm_a_rr_penalty_time = -1; +static int hf_gsm_a_rr_gprs_ra_colour = -1; +static int hf_gsm_a_rr_si13_position = -1; +static int hf_gsm_a_rr_power_offset = -1; +static int hf_gsm_a_rr_si2quater_position = -1; +static int hf_gsm_a_rr_si13alt_position = -1; +static int hf_gsm_a_rr_prio_thr = -1; +static int hf_gsm_a_rr_lsa_offset = -1; +static int hf_gsm_a_rr_paging_channel_restructuring = -1; +static int hf_gsm_a_rr_nln_sacch = -1; +static int hf_gsm_a_rr_nln_status_sacch = -1; +static int hf_gsm_a_rr_nln_pch = -1; +static int hf_gsm_a_rr_nln_status_pch = -1; +static int hf_gsm_a_rr_vbs_vgcs_inband_notifications = -1; +static int hf_gsm_a_rr_vbs_vgcs_inband_pagings = -1; +static int hf_gsm_a_rr_rac = -1; +static int hf_gsm_a_rr_max_lapdm = -1; +static int hf_gsm_a_rr_gprs_ms_txpwr_max_ccch = -1; +static int hf_gsm_a_rr_dedicated_mode_mbms_notification_support = -1; +static int hf_gsm_a_rr_mnci_support = -1; +static int hf_gsm_a_rr_amr_config = -1; +static int hf_gsm_a_rr_bcch_change_mark = -1; +static int hf_gsm_a_rr_si_change_field = -1; +static int hf_gsm_a_rr_si13_change_mark = -1; +static int hf_gsm_a_rr_hsn = -1; +static int hf_gsm_a_rr_rfl_number = -1; +static int hf_gsm_a_rr_arfcn_index = -1; +static int hf_gsm_a_rr_ma_length = -1; +static int hf_gsm_a_rr_psi1_repeat_period = -1; +static int hf_gsm_a_rr_pbcch_pb = -1; +static int hf_gsm_a_rr_pbcch_tsc = -1; +static int hf_gsm_a_rr_pbcch_tn = -1; +static int hf_gsm_a_rr_spgc_ccch_sup = -1; +static int hf_gsm_a_rr_priority_access_thr = -1; +static int hf_gsm_a_rr_nmo = -1; +static int hf_gsm_a_rr_t3168 = -1; +static int hf_gsm_a_rr_t3192 = -1; +static int hf_gsm_a_rr_drx_timer_max = -1; +static int hf_gsm_a_rr_access_burst_type = -1; +static int hf_gsm_a_rr_control_ack_type = -1; +static int hf_gsm_a_rr_bs_cv_max = -1; +static int hf_gsm_a_rr_pan_dec = -1; +static int hf_gsm_a_rr_pan_inc = -1; +static int hf_gsm_a_rr_pan_max = -1; +static int hf_gsm_a_rr_egprs_packet_channel_request = -1; +static int hf_gsm_a_rr_bep_period = -1; +static int hf_gsm_a_rr_pfc_feature_mode = -1; +static int hf_gsm_a_rr_dtm_support = -1; +static int hf_gsm_a_rr_bss_paging_coordination = -1; +static int hf_gsm_a_rr_ccn_active = -1; +static int hf_gsm_a_rr_nw_ext_utbf = -1; +static int hf_gsm_a_rr_multiple_tbf_capability = -1; +static int hf_gsm_a_rr_ext_utbf_no_data = -1; +static int hf_gsm_a_rr_dtm_enhancements_capability = -1; +static int hf_gsm_a_rr_reduced_latency_access = -1; +static int hf_gsm_a_rr_alpha = -1; +static int hf_gsm_a_rr_t_avg_w = -1; +static int hf_gsm_a_rr_t_avg_t = -1; +static int hf_gsm_a_rr_pc_meas_chan = -1; +static int hf_gsm_a_rr_n_avg_i = -1; +static int hf_gsm_a_rr_sgsnr = -1; +static int hf_gsm_a_rr_si_status_ind = -1; +static int hf_gsm_a_rr_lb_ms_txpwr_max_cch = -1; +static int hf_gsm_a_rr_si2n_support = -1; +static int hf_gsm_a_rr_mi_index = -1; +static int hf_gsm_a_rr_mi_count = -1; +static int hf_gsm_a_rr_3g_wait = -1; +static int hf_gsm_a_rr_qsearch_c = -1; +static int hf_gsm_a_rr_bsic_seen = -1; +static int hf_gsm_a_rr_scale = -1; +static int hf_gsm_a_rr_mean_bep_gmsk = -1; +static int hf_gsm_a_rr_mean_cv_bep = -1; +static int hf_gsm_a_rr_nbr_rcvd_blocks = -1; +static int hf_gsm_a_rr_reporting_quantity = -1; +static int hf_gsm_a_rr_extended_ra = -1; +static int hf_gsm_a_rr_access_tech_type = -1; +static int hf_gsm_a_rr_tfi_assignment = -1; +static int hf_gsm_a_rr_polling = -1; +static int hf_gsm_a_rr_usf = -1; +static int hf_gsm_a_rr_usf_granularity = -1; +static int hf_gsm_a_rr_p0 = -1; +static int hf_gsm_a_rr_pr_mode = -1; +static int hf_gsm_a_rr_egprs_mcs = -1; +static int hf_gsm_a_rr_tlli_block_channel_coding = -1; +static int hf_gsm_a_rr_bep_period2 = -1; +static int hf_gsm_a_rr_resegment = -1; +static int hf_gsm_a_rr_egprs_window_size = -1; +static int hf_gsm_a_rr_gamma = -1; +static int hf_gsm_a_rr_timing_adv_index = -1; +static int hf_gsm_a_rr_timing_adv_timeslot_num = -1; +static int hf_gsm_a_rr_tbf_starting_time = -1; +static int hf_gsm_a_rr_num_of_radio_block_allocated = -1; +static int hf_gsm_a_rr_pfi = -1; +static int hf_gsm_a_rr_mbms_service_id = -1; +static int hf_gsm_a_rr_ms_id = -1; +static int hf_gsm_a_rr_gprs_cs = -1; +static int hf_gsm_a_rr_rlc_mode = -1; +static int hf_gsm_a_rr_ta_valid = -1; +static int hf_gsm_a_rr_link_quality_meas_mode = -1; +static int hf_gsm_a_rr_emr_bitmap_length = -1; +static int hf_gsm_a_rr_eutran_mr_n_eutran = -1; +static int hf_gsm_a_rr_eutran_mr_freq_idx = -1; +static int hf_gsm_a_rr_eutran_mr_cell_id = -1; +static int hf_gsm_a_rr_eutran_mr_rpt_quantity = -1; +static int hf_gsm_a_rr_ma_channel_set = -1; +static int hf_n_range_orig_arfcn = -1; + + + +/* Additions in Rel-8 */ +static int hf_gsm_a_rr_3g_priority_param_desc_utran_start = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_utran_stop = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran = -1; +static int hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin = -1; +static int hf_gsm_a_rr_utran_frequency_index = -1; +static int hf_gsm_a_rr_utran_priority = -1; +static int hf_gsm_a_rr_thresh_utran_high = -1; +static int hf_gsm_a_rr_thresh_utran_low = -1; +static int hf_gsm_a_rr_utran_qrxlevmin = -1; +static int hf_gsm_a_rr_eutran_ccn_active = -1; +static int hf_gsm_a_rr_eutran_start = -1; +static int hf_gsm_a_rr_eutran_stop = -1; +static int hf_gsm_a_rr_qsearch_c_eutran_initial = -1; +static int hf_gsm_a_rr_eutran_rep_quant = -1; +static int hf_gsm_a_rr_eutran_multirat_reporting = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_eutran_fdd_reporting_offset = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_threshold_2 = -1; +static int hf_gsm_a_rr_eutran_tdd_reporting_offset = -1; +static int hf_gsm_a_rr_eutran_fdd_measurement_report_offset = -1; +static int hf_gsm_a_rr_eutran_tdd_measurement_report_offset = -1; +static int hf_gsm_a_rr_reporting_granularity = -1; +static int hf_gsm_a_rr_eutran_default_measurement_control_eutran = -1; +static int hf_gsm_a_rr_eutran_measurement_control_eutran = -1; +static int hf_gsm_a_rr_qsearch_p_eutran = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_geran_priority = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_h_prio = -1; +static int hf_gsm_a_rr_serving_cell_priority_param_t_reselection = -1; +static int hf_gsm_a_rr_eutran_earfcn = -1; +static int hf_gsm_a_rr_eutran_measurement_bandwidth = -1; +static int hf_gsm_a_rr_eutran_priority = -1; +static int hf_gsm_a_rr_thresh_eutran_high = -1; +static int hf_gsm_a_rr_thresh_eutran_low = -1; +static int hf_gsm_a_rr_eutran_qrxlevmin = -1; +static int hf_gsm_a_rr_eutran_pcid = -1; +static int hf_gsm_a_rr_eutran_pcid_bitmap_group = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern_length = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern = -1; +static int hf_gsm_a_rr_eutran_pcid_pattern_sense = -1; +static int hf_gsm_a_rr_eutran_frequency_index = -1; +static int hf_gsm_a_rr_psc = -1; +static int hf_gsm_a_rr_utran_psc_pattern_length = -1; +static int hf_gsm_a_rr_utran_psc_pattern_sense = -1; +static int hf_gsm_a_rr_utran_csg_fdd_uarfcn = -1; +static int hf_gsm_a_rr_utran_csg_tdd_uarfcn = -1; +static int hf_gsm_a_rr_csg_earfcn = -1; +static int hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran = -1; + +/* Initialize the subtree pointers */ +static gint ett_ccch_msg = -1; +static gint ett_ccch_oct_1 = -1; +static gint ett_sacch_msg = -1; + +static char a_bigbuf[1024]; + +static dissector_handle_t data_handle; +static dissector_handle_t rrlp_dissector; + + +#define NUM_GSM_RR_ELEM (sizeof(gsm_rr_elem_strings)/sizeof(value_string)) +gint ett_gsm_rr_elem[NUM_GSM_RR_ELEM]; + +typedef enum +{ + /* RR Rest Octets information elements */ + DE_RR_REST_OCTETS_UTRAN_FDD_DESC, + DE_RR_REST_OCTETS_UTRAN_TDD_DESC, + DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_GPRS_RTD_DESC, + DE_RR_REST_OCTETS_GPRS_BSIC_DESC, + DE_RR_REST_OCTETS_GPRS_REPORT_PRIO_DESC, + DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_NC_MEAS_PARAM, + DE_RR_REST_OCTETS_SI2Q_EXT_INFO, + DE_RR_REST_OCTETS_CCN_SUPPORT_DESC, + DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC, + DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD, + DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD, + DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC, + DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2, + DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC, + DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC, + DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC, + DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM, + DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC, + DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS, + DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS, + DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING, + DE_RR_REST_OCTETS_3G_CSG_DESC, + DE_RR_REST_OCTETS_EUTRAN_CSG_DESC, + DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM, + DE_RR_REST_OCTETS_GPRS_INDICATOR, + DE_RR_REST_OCTETS_SI4_REST_OCTETS_O, + DE_RR_REST_OCTETS_SI4_REST_OCTETS_S, + DE_RR_REST_OCTETS_LSA_PARAMETERS, + DE_RR_REST_OCTETS_LSA_ID_INFO, + DE_RR_REST_OCTETS_PCH_AND_NCH_INFO, + DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS, + DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC, + DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS, + DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO, + DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS, + DE_RR_REST_OCTETS_PBCCH_DESC, + DE_RR_REST_OCTETS_GSM_DESC, + DE_RR_REST_OCTETS_RTD_DESC, + DE_RR_REST_OCTETS_BSIC_DESC, + DE_RR_REST_OCTETS_REPORT_PRIO_DESC, + DE_RR_REST_OCTETS_CDMA2000_DESC, + DE_RR_REST_OCTETS_SERVING_CELL_DATA, + DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO, + DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING, + DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC, + DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC, + DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_TMGI, + DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE, + DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT, + DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT, + DE_RR_REST_OCTETS_REPORTING_QUANTITY, + DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT, + DE_RR_REST_OCTETS_NONE +} +rr_rest_octets_elem_idx_t; + +#define NUM_GSM_RR_REST_OCTETS_ELEM (sizeof(gsm_rr_rest_octets_elem_strings)/sizeof(value_string)) +gint ett_gsm_rr_rest_octets_elem[NUM_GSM_RR_REST_OCTETS_ELEM]; + +/* + * Generates a string representing the bits in a bitfield at "bit_offset" from an 8 bit boundary + * with the length in bits of no_of_bits based on value. + * Ex: ..xx x... + */ + +/* this function is used for dissecting the 0/1 presence flags in CSN.1 coded IEs */ +static gboolean gsm_rr_csn_flag(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, const char *description, const char *true_string, const char * false_string) +{ + guint8 bit_mask = 0x80 >> (bit_offset % 8); + guint8 value = tvb_get_guint8(tvb, bit_offset >> 3); + char bits_str[] = {".... ...."}; + guint8 offset_in_octet = bit_offset % 8; + + if (value & bit_mask) + { + bits_str[offset_in_octet + (offset_in_octet / 4)] = '1'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, true_string); + return TRUE; + } + bits_str[offset_in_octet + (offset_in_octet / 4)] = '0'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, false_string); + return FALSE; +} + +/* this function is used for dissecting the H/L presence flags in CSN.1 coded IEs" + If truncation ( 44.018 section 8.9) is allowed, truncation_length is set to the actual length of the PDU, + otherwise it is set to 0 */ +static gboolean gsm_rr_csn_HL_flag(tvbuff_t *tvb, proto_tree *tree, guint8 truncation_length, gint bit_offset, const char *description, const char *true_string, const char * false_string) +{ + guint8 bit_mask = 0x80 >> (bit_offset % 8); + guint8 value = 0; + char bits_str[] = {".... ...."}; + guint8 offset_in_octet = bit_offset % 8; + + /* if truncation_length == 0, then don't check for truncation*/ + if ((!truncation_length)||((bit_offset>>3) < truncation_length)) + { + value = tvb_get_guint8(tvb, bit_offset >> 3)^PADDING_BYTE; + } + if (value & bit_mask) + { + bits_str[offset_in_octet + (offset_in_octet / 4)] = 'H'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, true_string); + return TRUE; + } + bits_str[offset_in_octet + (offset_in_octet / 4)] = 'L'; + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "%s: %s: %s", bits_str, description, false_string); + return FALSE; +} + +/* +10.5.2 Radio Resource management information elements + * [3] 10.5.2.1a BA Range + */ +static guint16 +de_rr_ba_range(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint8 value; + + curr_offset = offset; + proto_tree_add_item(tree, hf_gsm_a_rr_range_nb, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + value = tvb_get_guint8(tvb, curr_offset); + curr_offset += 1; + bit_offset = curr_offset << 3; + while (value) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_lower, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_higher, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + value -= 1; + } + + curr_offset += len - 1; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1b Cell Channel Description + */ + +#define ARFCN_MAX 1024 /* total number of ARFCNs defined */ + +static void display_channel_list(guint8 *list, tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len) +{ + int arfcn; + proto_item *ti=NULL; + + ti = proto_tree_add_text(tree, tvb, offset, len, "List of ARFCNs ="); + for (arfcn=0; arfcn> 3; + guint8 len = tvb_length(tvb); + + if (len > octet_offset) + { + /* there is spare room, check the first octet */ + guint8 bit_mask = 0xFF >> (bit_offset & 0x07); + if ((tvb_get_guint8(tvb, octet_offset) & bit_mask) != (PADDING_BYTE & bit_mask)) + found = TRUE; + + for (i=octet_offset+1; (i>= 1; + return j; +} + +static gint f_k(gint k, gint *w, gint range) +{ + gint idx, n, j; + + idx = k; + range -= 1; + range = range/greatest_power_of_2_lesser_or_equal_to(idx); + n = w[idx]-1; + + while (idx>1) { + j = greatest_power_of_2_lesser_or_equal_to(idx); + range = 2*range+1; + if ((2*idx) < 3*j){ /* left child */ + idx -= j/2; + n = (n+w[idx]-1+((range-1)/2)+1)%range; + } + else { /* right child */ + idx -= j; + n = (n+w[idx]-1+1)%range; + } + } + + return (n+1)%1024; +} + +static void dissect_channel_list_n_range(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gint range) +{ + gint curr_offset=offset, bit_offset, f0, arfcn_orig, w[64], wsize, i; + gint octet, nwi=1, jwi=0, imax, iused, arfcn; + guint8 list[1024]; + proto_item *item; + proto_tree *subtree; + + memset((void*)list,0,sizeof(list)); + + item = proto_tree_add_text(tree,tvb, curr_offset, len, "Range %d format", range); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_NEIGH_CELL_DESC]); + + octet = tvb_get_guint8(tvb, curr_offset); + if (range==1024) { + f0 = (octet>>2)&1; + if (f0) + list[0] = 1; + arfcn_orig = 0; + wsize = 10; + imax = 16; + bit_offset = curr_offset*8 + 6; + } + else { + bit_offset = curr_offset*8 + 7; + arfcn_orig = (gint) tvb_get_bits(tvb, bit_offset, 10, FALSE); + proto_tree_add_bits_item(subtree, hf_n_range_orig_arfcn, tvb, bit_offset, 10, FALSE); + bit_offset+=10; + + list[arfcn_orig] = 1; + + switch (range) { + case 512: + wsize=9; + imax = 17; + break; + case 256: + wsize=8; + imax = 21; + break; + case 128: + wsize=7; + imax = 28; + break; + default: + wsize=0; + imax = 0; + DISSECTOR_ASSERT_NOT_REACHED(); + } + } + iused = imax; /* in case the list is actually full */ + + /* extract the variable size w[] elements */ + for (i=1; i<=imax; i++) { + w[i] = (gint) tvb_get_bits(tvb, bit_offset, wsize, FALSE); + proto_tree_add_text(subtree, tvb, bit_offset>>3, ((bit_offset+wsize-1)>>3) - (bit_offset>>3) + 1 , "%s %s(%d): %d", + decode_bits_in_field(bit_offset, wsize, w[i]), + "W", + i, + w[i]); + bit_offset += wsize; + curr_offset = bit_offset>>3; + + if ((iused == imax) && (w[i] == 0) ) { + iused = i - 1; + } + if ((curr_offset-offset)>len) { + iused = i - 1; + break; + } + if (++jwi==nwi) { /* check if the number of wi at this wsize has been extracted */ + jwi = 0; /* reset the count of wi at this size */ + nwi <<= 1; /* get twice as many of the next size */ + wsize--; /* make the next size 1 bit smaller */ + } + } + + for (i=1; i<=iused; i++) { + arfcn = (f_k(i, w, range) + arfcn_orig)%1024; + list[arfcn] = 1; + } + + display_channel_list(list, tvb, tree, offset, curr_offset-offset); + + return; +} + +static guint16 +dissect_arfcn_list_core(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_, guint8 format) +{ + guint32 curr_offset,byte; + guint8 oct,bit; + guint16 arfcn; + proto_item *item; + + curr_offset = offset; + + if ((format & 0xc0) == 0x00) + { + /* bit map 0 */ + item = proto_tree_add_text(tree,tvb, curr_offset, len, "List of ARFCNs ="); + bit = 4; + arfcn = 125; + for (byte = 0;byte <= len-1;byte++) + { + oct = tvb_get_guint8(tvb, curr_offset); + while (bit-- != 0) + { + arfcn--; + if (((oct >> bit) & 1) == 1) + { + proto_item_append_text(item," %d",arfcn); + } + } + bit = 8; + curr_offset++; + } + } + else if ((format & 0xc8) == 0x80) + { + /* 1024 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 1024); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x88) + { + /* 512 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 512); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8a) + { + /* 256 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 256); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8c) + { + /* 128 range */ + dissect_channel_list_n_range(tvb, tree, pinfo, curr_offset, len, 128); + curr_offset = curr_offset + len; + } + else if ((format & 0xce) == 0x8e) + { + /* variable bit map */ + arfcn = ((format & 0x01) << 9) | (tvb_get_guint8(tvb, curr_offset+1) << 1) | ((tvb_get_guint8(tvb, curr_offset + 2) & 0x80) >> 7); + item = proto_tree_add_text(tree,tvb,curr_offset,len,"List of ARFCNs = %d",arfcn); + curr_offset = curr_offset + 2; + bit = 7; + for (byte = 0;byte <= len-3;byte++) + { + oct = tvb_get_guint8(tvb, curr_offset); + while (bit-- != 0) + { + arfcn++; + if (((oct >> bit) & 1) == 1) + { + proto_item_append_text(item," %d",arfcn); + } + } + bit = 8; + curr_offset++; + } + } + + return(curr_offset - offset); +} + +/* + * Format ID is in bits: + * 128 127 124 123 122 (hf_gsm_a_rr_format_id) + */ +static guint16 +dissect_arfcn_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + + curr_offset = offset; + + oct = tvb_get_guint8(tvb, curr_offset); + + /* FORMAT-ID, Format Identifier (part of octet 3)*/ + proto_tree_add_item(tree, hf_gsm_a_rr_format_id, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += dissect_arfcn_list_core(tvb, tree, pinfo, offset, len, add_string, string_len, oct); + + return(curr_offset - offset); +} + +/* + * Format ID is in bits: + * 128 124 123 122 (hf_gsm_a_rr_format_id2) + */ +static guint16 +dissect_arfcn_list2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + + curr_offset = offset; + + /* Turn bit 127 off, in order to reuse the ARFCN dissection code */ + oct = tvb_get_guint8(tvb, curr_offset) & 0xbf; + + /* FORMAT-ID, Format Identifier (part of octet 3)*/ + proto_tree_add_item(tree, hf_gsm_a_rr_format_id2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += dissect_arfcn_list_core(tvb, tree, pinfo, offset, len, add_string, string_len, oct); + + return(curr_offset - offset); +} + +static guint16 +de_rr_cell_ch_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} +/* + * [3] 10.5.2.1c BA List Pref + */ +static guint16 +de_rr_ba_list_pref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + bit_offset = curr_offset << 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_list_pref_length, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating Range Limits", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_lower, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_range_higher, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating BA Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_freq, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1d UTRAN Frequency List + */ +static guint16 +de_rr_utran_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + /* < UTRAN Freq List >::= + * < LENGTH OF UTRAN FREQ LIST : bit (8) > -- length following in octets + * { 1 < FDD_ARFCN > : bit (14) } ** 0 -- FDD frequencies + * { 1 < TDD_ARFCN > : bit (14) } ** 0 -- TDD frequencies + * **; + * Spare bits in the end of the field are used to fill the last octet. + */ + bit_offset = curr_offset << 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_utran_freq_list_length, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating FDD Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating TDD Frequency", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH + */ +static const guint8 +convert_n_to_p[32] = { 0, 10, 19, 28, 36, 44, 52, 60, 67, 74, 81, 88, 95, 102, 109, 116, + 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const guint8 +convert_n_to_q[32] = { 0, 9, 17, 25, 32, 39, 46, 53, 59, 65, 71, 77, 83, 89, 95, 101, + 106, 111, 116, 121, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static guint16 +de_rr_cell_select_indic(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, idx, xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + guint8 value; + + curr_offset = offset; + bit_offset = curr_offset << 3; + value = tvb_get_bits8(tvb,bit_offset,3); + bit_offset += 3; + switch (value) + { + case 0: /* GSM Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GSM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GSM_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating GSM Description struct", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Band Indicator", "1900", "1800"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_arfcn, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + case 1: /* UTRAN FDD Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating UTRAN FDD Description struct", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_fdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Cell Information", "Present", "Not Present")) + { + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Indic0", "1", "0"); + idx = tvb_get_bits8(tvb,bit_offset,5); + proto_tree_add_text(subtree,tvb, bit_offset>>3, 1, "Nr of FDD Cells : %d", idx); + bit_offset += 5; + idx = convert_n_to_p[idx]; + item2 = proto_tree_add_text(subtree,tvb, bit_offset>>3, (idx>>3)+1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Scrambling Code: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Diversity: %d", 0); + } + if (idx) + { + wsize = 10; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, bit_offset, wsize, ENC_BIG_ENDIAN); + bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 1024); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Scrambling Code: %d", xdd_cell_info & 0x01FF); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Diversity: %d", (xdd_cell_info >> 9) & 0x01); + } + } + } + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + case 2: /* UTRAN TDD Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeating UTRAN TDD Description struct", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_tdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Cell Information", "Present", "Not Present")) + { + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Indic0", "1", "0"); + idx = tvb_get_bits8(tvb,bit_offset,5); + proto_tree_add_text(subtree,tvb, bit_offset>>3, 1, "Nr of TDD Cells : %d", idx); + bit_offset += 5; + idx = convert_n_to_q[idx]; + item2 = proto_tree_add_text(subtree,tvb, bit_offset>>3, (idx>>3)+1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Cell Parameter: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Sync Case TSTD: %d", 0); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, "Diversity TDD: %d", 0); + } + if (idx) + { + wsize = 9; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, bit_offset, wsize, ENC_BIG_ENDIAN); + bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 512); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Cell Parameter: %d", xdd_cell_info & 0x07F); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Sync Case TSTD: %d", (xdd_cell_info >> 7) & 0x01); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 0, + "Diversity TDD: %d", (xdd_cell_info >> 8) & 0x01); + } + } + } + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + break; + default: + break; + } + + curr_offset += len; + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.2 Cell Description + */ +guint16 +de_rr_cell_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + guint16 bcch_arfcn; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 2, "%s", + gsm_rr_elem_strings[DE_RR_CELL_DSC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_DSC]); + + proto_tree_add_item(subtree, hf_gsm_a_ncc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_bcc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + bcch_arfcn = (tvb_get_guint8(tvb,curr_offset) & 0xc0) << 2; + bcch_arfcn = bcch_arfcn | tvb_get_guint8(tvb,curr_offset+1); + proto_tree_add_uint(subtree, hf_gsm_a_bcch_arfcn , tvb, curr_offset, 2, bcch_arfcn ); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.3 Cell Options (BCCH) + */ +static const value_string gsm_a_rr_dtx_bcch_vals[] = { + { 0x00, "The MSs may use uplink discontinuous transmission" }, + { 0x01, "The MSs shall use uplink discontinuous transmission" }, + { 0x02, "The MSs shall not use uplink discontinuous transmission" }, + { 0x03, "Reserved" }, + { 0, NULL } }; + +static const value_string gsm_a_rr_radio_link_timeout_vals[] = { + { 0x00, "4" }, + { 0x01, "8" }, + { 0x02, "12" }, + { 0x03, "16" }, + { 0x04, "20" }, + { 0x05, "24" }, + { 0x06, "28" }, + { 0x07, "32" }, + { 0x08, "36" }, + { 0x09, "40" }, + { 0x0A, "44" }, + { 0x0B, "48" }, + { 0x0C, "52" }, + { 0x0D, "56" }, + { 0x0E, "60" }, + { 0x0F, "64" }, + { 0, NULL } }; + +static guint16 +de_rr_cell_opt_bcch(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CELL_OPT_BCCH].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_OPT_BCCH]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pwrc, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_dtx_bcch, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_radio_link_timeout, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.3a Cell Options (SACCH) + */ +static const value_string gsm_a_rr_dtx_sacch_vals[] = { + { 0x00, "The MS may use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x01, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x02, "The MS shall not use uplink discontinuous transmission on a TCH-F. The MS shall not use uplink discontinuous transmission on TCH-H" }, + { 0x03, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS may use uplink discontinuous transmission on TCH-H" }, + { 0x04, "The MS may use uplink discontinuous transmission on a TCH-F. The MS may use uplink discontinuous transmission on TCH-H" }, + { 0x05, "The MS shall use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0x06, "The MS shall not use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0x07, "The MS may use uplink discontinuous transmission on a TCH-F. The MS shall use uplink discontinuous transmission on TCH-H" }, + { 0, NULL } }; + +static guint16 +de_rr_cell_opt_sacch(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint8 oct; + guint8 dtx; + guint32 curr_offset; + + curr_offset = offset; + + oct = tvb_get_guint8(tvb, curr_offset); + dtx = ((oct&0x80)>>5)|((oct&0x30)>>4); /* DTX is a split filed in bits 8, 6 and 5 */ + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CELL_OPT_SACCH].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_OPT_SACCH]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pwrc, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree, hf_gsm_a_rr_dtx_sacch, tvb, curr_offset, 1, dtx); + proto_tree_add_item(subtree, hf_gsm_a_rr_radio_link_timeout, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.4 Cell Selection Parameters + */ +static guint16 +de_rr_cell_sel_param(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 2, "%s", + gsm_rr_elem_strings[DE_RR_CELL_SEL_PARAM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CELL_SEL_PARAM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_cell_reselect_hyst, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_ms_txpwr_max_cch, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_acs, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_neci, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_rxlev_access_min, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.4a MAC Mode and Channel Coding Requested + * [3] 10.5.2.5 Channel Description + */ +guint16 +de_rr_ch_dsc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8,subchannel; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + + if ((oct8 & 0xf8) == 0x08) + { + str = "TCH/F + ACCHs"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else + { + if ((oct8 & 0xf0) == 0x10) + { + str = "TCH/H + ACCHs, Subchannel"; + subchannel = ((oct8 & 0x08)>>3); + } + else if ((oct8 & 0xe0) == 0x20) + { + str = "SDCCH/4 + SACCH/C4 or CBCH (SDCCH/4), Subchannel"; + subchannel = ((oct8 & 0x18)>>3); + } + else if ((oct8 & 0xc0) == 0x40) + { + str = "SDCCH/8 + SACCH/C8 or CBCH (SDCCH/8), Subchannel"; + subchannel = ((oct8 & 0x38)>>3); + } else { + str = "Unknown channel information"; + subchannel = oct8; + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s %d",a_bigbuf,str,subchannel); + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.5a Channel Description 2 + */ +static guint16 +de_rr_ch_dsc2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8,subchannel; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC2].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC2]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + + if ((oct8 & 0xf8) == 0x0) + { + str = "TCH/F + FACCH/F and SACCH/M"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else if ((oct8 & 0xf8) == 0x08) + { + str = "TCH/F + FACCH/F and SACCH/F"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else if ((oct8 & 0xf8) == 0xf0) + { + str = "TCH/F + FACCH/F and SACCH/M + bi- and unidirectional channels"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + } + else + { + if ((oct8 & 0xf0) == 0x10) + { + str = "TCH/H + ACCHs, Subchannel"; + subchannel = ((oct8 & 0x08)>>3); + } + else if ((oct8 & 0xe0) == 0x20) + { + str = "SDCCH/4 + SACCH/C4 or CBCH (SDCCH/4), Subchannel"; + subchannel = ((oct8 & 0x18)>>3); + } + else if ((oct8 & 0xc0) == 0x40) + { + str = "SDCCH/8 + SACCH/C8 or CBCH (SDCCH/8), Subchannel"; + subchannel = ((oct8 % 0x38)>>3); + } + else if ((oct8 & 0xc0) == 0x80) + { + str = "TCH/F + FACCH/F and SACCH/M + bidirectional channels at timeslot"; + subchannel = ((oct8 % 0x38)>>3); + } + else if ((oct8 & 0xe0) == 0xc0) + { + str = "TCH/F + FACCH/F and SACCH/M + unidirectional channels at timeslot"; + subchannel = ((oct8 % 0x38)>>3); + } else { + str = "Unknown channel information"; + subchannel = oct8; + } + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s %d",a_bigbuf,str,subchannel); + } + + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.5c Channel Description 3 + */ +static guint16 +de_rr_ch_dsc3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 3, "%s", gsm_rr_elem_strings[DE_RR_CH_DSC3].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CH_DSC3]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.6 Channel Mode + */ +/* Channel Mode */ +static const value_string gsm_a_rr_channel_mode_vals[] = { + { 0x00, "signalling only"}, + { 0x01, "speech full rate or half rate version 1(GSM FR or GSM HR)"}, + { 0x21, "speech full rate or half rate version 2(GSM EFR)"}, + { 0x41, "speech full rate or half rate version 3(FR AMR or HR AMR)"}, + { 0x81, "speech full rate or half rate version 4(OFR AMR-WB or OHR AMR-WB)"}, + { 0x82, "speech full rate or half rate version 5(FR AMR-WB )"}, + { 0x83, "speech full rate or half rate version 6(OHR AMR )"}, + { 0x61, "data, 43.5 kbit/s (downlink)+14.5 kbps (uplink)"}, + { 0x62, "data, 29.0 kbit/s (downlink)+14.5 kbps (uplink)"}, + { 0x64, "data, 43.5 kbit/s (downlink)+29.0 kbps (uplink)"}, + { 0x67, "data, 14.5 kbit/s (downlink)+43.5 kbps (uplink)"}, + { 0x65, "data, 14.5 kbit/s (downlink)+29.0 kbps (uplink)"}, + { 0x66, "data, 29.0 kbit/s (downlink)+43.5 kbps (uplink)"}, + { 0x27, "data, 43.5 kbit/s radio interface rate"}, + { 0x63, "data, 32.0 kbit/s radio interface rate"}, + { 0x43, "data, 29.0 kbit/s radio interface rate"}, + { 0x0f, "data, 14.5 kbit/s radio interface rate"}, + { 0x03, "data, 12.0 kbit/s radio interface rate"}, + { 0x0b, "data, 6.0 kbit/s radio interface rate"}, + { 0x13, "data, 3.6 kbit/s radio interface rate"}, + { 0, NULL } +}; + +guint16 +de_rr_ch_mode(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_channel_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.7 Channel Mode 2 + */ + +static const value_string gsm_a_rr_channel_mode2_vals[] = { + { 0x00, "signalling only"}, + { 0x05, "speech half rate version 1(GSM HR)"}, + { 0x25, "speech half rate version 2(GSM EFR)"}, + { 0x45, "speech half rate version 3(HR AMR)"}, + { 0x85, "speech half rate version 4(OHR AMR-WB)"}, + { 0x06, "speech half rate version 6(OHR AMR )"}, + { 0x0f, "data, 6.0 kbit/s radio interface rate"}, + { 0x17, "data, 3.6 kbit/s radio interface rate"}, + { 0, NULL } +}; + +static guint16 +de_rr_ch_mode2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_channel_mode2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.7a UTRAN Classmark information element + */ +static guint16 +de_rr_utran_cm(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + tvbuff_t *rrc_irat_ho_info_tvb; + + curr_offset = offset; + if (len) + { + rrc_irat_ho_info_tvb = tvb_new_subset(tvb, curr_offset, len, len); + if (rrc_irat_ho_info_handle) + call_dissector(rrc_irat_ho_info_handle, rrc_irat_ho_info_tvb, pinfo, tree); + } + + curr_offset += len; + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.7b (void) + */ + +/* + * [3] 10.5.2.7c Classmark Enquiry Mask + * Bit 8: + * 0 CLASSMARK CHANGE message is requested + * 1 CLASSMARK CHANGE message is not requested + * Bits 7-5 . 5 + * 000 UTRAN CLASSMARK CHANGE message including status on predefined configurations (i.e. Sequence Description) is requested + * 111 UTRAN CLASSMARK CHANGE message including status on predefined configurations (i.e. Sequence Description) is not requested. + * All other values shall not be sent. If received, they shall be interpreted as '000'. + * Bit 4: + * 0 CDMA2000 CLASSMARK CHANGE message requested + * 1 CDMA2000 CLASSMARK CHANGE message not requested. + * Bit 3: + * 0 GERAN IU MODE CLASSMARK CHANGE message requested + * 1 GERAN IU MODE CLASSMARK CHANGE message not requested. + * Bits 2 - 1: spare(0). + */ +static const true_false_string gsm_a_msg_req_value = { + "message is not requested", + "message is requested" +}; + +static const value_string gsm_a_rr_utran_cm_cng_msg_req_vals[] = { + { 0x0, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x1, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x2, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x3, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x4, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x5, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x6, "message including status on predefined configurations (i.e. Sequence Description) is requested"}, + { 0x7, "message including status on predefined configurations (i.e. Sequence Description) is not requested."}, + { 0, NULL } +}; +guint16 +de_rr_cm_enq_mask(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_utran_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_cdma200_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_geran_iu_cm_cng_msg_req, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.8 Channel Needed + */ +static const value_string gsm_a_rr_channel_needed_vals[] = { + { 0x00, "Any channel"}, + { 0x01, "SDCCH"}, + { 0x02, "TCH/F (Full rate)"}, + { 0x03, "TCH/H or TCH/F (Dual rate)"}, + { 0, NULL } +}; +guint16 +de_rr_chnl_needed(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_CHNL_NEEDED].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CHNL_NEEDED]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch1, tvb, (curr_offset<<3)+bit_offset+2, 2, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch2, tvb, (curr_offset<<3)+bit_offset, 2, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.8a Channel Request Description + * [3] 10.5.2.8b Channel Request Description 2 + */ +/* + * [3] 10.5.2.9 Cipher Mode Setting + */ +/* SC (octet 1) */ +static const value_string gsm_a_rr_sc_vals[] = { + { 0, "No ciphering"}, + { 1, "Start ciphering"}, + { 0, NULL } +}; +/* algorithm identifier + * If SC=1 then: + * bits + * 4 3 2 + */ +guint16 +de_rr_cip_mode_set(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint64 value; + + curr_offset = offset; + + /* Cipher Mode Setting + * Note: The coding of fields SC and algorithm identifier is defined in [44.018] + * as part of the Cipher Mode Setting IE. + */ + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + proto_tree_add_bits_ret_val(tree, hf_gsm_a_rr_sc, tvb, (curr_offset<<3)+bit_offset+3, 1, &value, ENC_BIG_ENDIAN); + if (value == 1){ /* Start ciphering */ + /* algorithm identifier */ + proto_tree_add_bits_item(tree, hf_gsm_a_algorithm_id, tvb, (curr_offset<<3)+bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.10 Cipher Response + */ +/* CR (octet 1) */ +static const value_string gsm_a_rr_cr_vals[] = { + { 0, "IMEISV shall not be included"}, + { 1, "IMEISV shall be included"}, + { 0, NULL } +}; + +static guint16 +de_rr_cip_mode_resp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + if (RIGHT_NIBBLE==len) + bit_offset = 4; + else + bit_offset = 0; + + /* Cipher Mode Response + * Note: The coding of field CR is defined in [44.018] + * as part of the Cipher Mode Response IE. + */ + proto_tree_add_bits_item(tree, hf_gsm_a_rr_cr, tvb, (curr_offset<<3)+bit_offset+3, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* [3] 10.5.2.11 Control Channel Description */ + +static const value_string gsm_a_rr_mscr_vals[] = { + { 0, "MSC is Release '98 or older"}, + { 1, "MSC is Release '99 onwards"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_att_vals[] = { + { 0, "MSs in the cell are not allowed to apply IMSI attach and detach procedure"}, + { 1, "MSs in the cell shall apply IMSI attach and detach procedure"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_ccch_conf_vals[] = { + { 0, "1 basic physical channel used for CCCH, not combined with SDCCHs"}, + { 1, "1 basic physical channel used for CCCH, combined with SDCCHs"}, + { 2, "2 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 3, "Reserved"}, + { 4, "3 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 5, "Reserved"}, + { 6, "4 basic physical channels used for CCCH, not combined with SDCCHs"}, + { 7, "Reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cbq3_vals[] = { + { 0, "Iu mode not supported"}, + { 1, "Iu mode capable MSs barred"}, + { 2, "Iu mode supported, cell not barred"}, + { 3, "Iu mode supported, cell not barred"}, + { 0, NULL } +}; + +static guint16 +de_rr_ctrl_ch_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint8 oct; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_CTRL_CH_DESC].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CTRL_CH_DESC]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_mscr, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_att, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_bs_ag_blks_res, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_ccch_conf, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + oct = tvb_get_guint8(tvb, curr_offset); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq3, tvb, (curr_offset<<3)+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree, hf_gsm_a_rr_bs_pa_mfrms, tvb, curr_offset, 1, (oct&0x07)+2); + + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_t3212, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* [3] 10.5.2.11a DTM Information Details + */ +/* + * [3] 10.5.2.11b Dynamic ARFCN Mapping + */ +static const value_string gsm_a_rr_gsm_band_vals[] = { + { 0, "GSM 750"}, + { 1, "DCS 1800"}, + { 2, "PCS 1900"}, + { 3, "GSM T 380"}, + { 4, "GSM T 410"}, + { 5, "GSM T 900"}, + { 6, "GSM 710"}, + { 7, "GSM T 810"}, + { 8, "Reserved"}, + { 9, "Reserved"}, + { 10, "Reserved"}, + { 11, "Reserved"}, + { 12, "Reserved"}, + { 13, "Reserved"}, + { 14, "Reserved"}, + { 15, "Reserved"}, + { 0, NULL } +}; + + +static guint16 +de_rr_dyn_arfcn_map(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + guint64 length; + guint value; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_ret_val(tree, hf_gsm_a_rr_dyn_arfcn_length, tvb, bit_offset, 8, &length, ENC_BIG_ENDIAN); + value = gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeating Dynamic ARFCN Mapping", "Present", "Not Present"); + while (value && length) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_gsm_band, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_arfcn_first, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_band_offset, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_arfcn_range, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + length -= 4; + } + + curr_offset = curr_offset + len; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.12 Frequency Channel Sequence + */ +static guint16 +de_rr_freq_ch_seq(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset, i; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_lowest_arfcn, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset += 1; + bit_offset = curr_offset << 3; + for (i=0; i<16; i++) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_inc_skip_arfcn, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + + curr_offset = curr_offset + 8; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.13 Frequency List + */ +/* + * [3] 10.5.2.13 Frequency List + * + * Bit Bit Bit Bit Bit format notation + * 8 7 4 3 2 + * 0 0 X X X bit map 0 + * 1 0 0 X X 1024 range + * 1 0 1 0 0 512 range + * 1 0 1 0 1 256 range + * 1 0 1 1 0 128 range + * 1 0 1 1 1 variable bit map + */ +/* The mask 0xce (1100 1110) will produce the result 0110 0111*/ +static const value_string gsm_a_rr_freq_list_format_id_vals[] = { + { 0x00, "bit map 0"}, + { 0x02, "bit map 0"}, + { 0x04, "bit map 0"}, + { 0x06, "bit map 0"}, + { 0x08, "bit map 0"}, + { 0x0a, "bit map 0"}, + { 0x0c, "bit map 0"}, + { 0x0e, "bit map 0"}, + { 0x40, "1024 range"}, + { 0x41, "1024 range"}, + { 0x42, "1024 range"}, + { 0x43, "1024 range"}, + { 0x44, "512 range"}, + { 0x45, "256 range"}, + { 0x46, "128 range"}, + { 0x47, "variable bit map"}, + { 0x00, NULL } +}; + +static guint16 +de_rr_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, len, add_string, string_len); +} +/* + * [3] 10.5.2.14 Frequency Short List + * + *The Frequency Short List information element is a type 3 information element of 10 octet length. + * + * This element is encoded exactly as the Frequency List information element, + * except that it has a fixed length instead of a variable length and does + * not contain a length indicator and that it shall not be encoded in bitmap 0 format. + */ + + static guint16 +de_rr_freq_short_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) + { + return dissect_arfcn_list(tvb, tree, pinfo, offset, 9, add_string, string_len); + } + +/* + * [3] 10.5.2.14a Frequency Short List 2 + * + * The Frequency Short List information element is a type 3 information element of 8 octet length. + * + * This element is encoded exactly as the Frequency List information element, + * except that it has a fixed length instead of a variable length and does + * not contain a length indicator and that it shall not be encoded in bitmap 0 format. + */ +static guint16 +de_rr_freq_short_list2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + return dissect_arfcn_list(tvb, tree, pinfo, offset, 8, add_string, string_len); +} +/* + * [3] 10.5.2.14b Group Channel Description + */ + +/* + * [3] 10.5.2.14c GPRS Resumption + */ +static const true_false_string gsm_a_rr_gprs_resumption_ack_value = { + "Resumption of GPRS services successfully acknowledged", + "Resumption of GPRS services not successfully acknowledged" +}; + +static guint16 +de_rr_gprs_resumption(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_gprs_resumption_ack, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset += 1; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.14d GPRS broadcast information + */ + +static gint +de_rr_rest_oct_gprs_cell_options(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, curr_bit_offset_sav; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nmo, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t3168, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t3192, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_drx_timer_max, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_access_burst_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_control_ack_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bs_cv_max, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PAN bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_dec, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_inc, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pan_max, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Optional Extensions", "Present", "Not Present")) + { /* Optional extension information */ + curr_bit_offset_sav = curr_bit_offset; + item2 = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, -1, "%s", + gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_CELL_OPTIONS_EXT_INFO]); + value = tvb_get_bits8(tvb,curr_bit_offset,6); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 1, "Extension Length: %d", value); + curr_bit_offset += 6; + value += 1; + proto_item_set_len(item2,((curr_bit_offset+value-curr_bit_offset_sav)>>3)+1); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "EGPRS", "supported by cell", "not supported by cell")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_egprs_packet_channel_request, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bep_period, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + value -= 5; + } + value -= 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pfc_feature_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_dtm_support, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bss_paging_coordination, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 3; + if (value > 0) + { /* Rel 4 extension */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_ccn_active, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nw_ext_utbf, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 2; + if (value > 0) + { /* Rel 6 extension */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_multiple_tbf_capability, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_ext_utbf_no_data, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_dtm_enhancements_capability, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + value -= 3; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MBMS procedures", "supported by cell", "not supported by cell")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dedicated_mode_mbms_notification_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mnci_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + value -= 2; + } + else + bit_offset += 1; + value -= 1; + if (value > 0) + { /* Rel 7 extension */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reduced_latency_access, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + value -= 1; + } + } + } + curr_bit_offset += value; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_rest_oct_gprs_power_control_parameters(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_POWER_CONTROL_PARAMS]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t_avg_w, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_t_avg_t, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pc_meas_chan, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_n_avg_i, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static guint16 +de_rr_gprs_broadcast_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len , gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + bit_offset += de_rr_rest_oct_gprs_cell_options(tvb, tree, bit_offset); + bit_offset += de_rr_rest_oct_gprs_power_control_parameters(tvb, tree, bit_offset); + curr_offset += len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.15 Handover Reference + */ +static guint16 +de_rr_ho_ref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_HO_REF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_HO_REF]); + + /* Handover reference value */ + proto_tree_add_item(subtree, hf_gsm_a_rr_ho_ref_val, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +static const value_string gsm_a_access_tech_type_vals[] = { + { 0, "GSM P"}, + { 1, "GSM E --note that GSM E covers GSM P"}, + { 2, "GSM R --note that GSM R covers GSM E and GSM P"}, + { 3, "GSM 1800"}, + { 4, "GSM 1900"}, + { 5, "GSM 450"}, + { 6, "GSM 480"}, + { 7, "GSM 850"}, + { 8, "GSM 750"}, + { 9, "GSM T 380"}, + { 10, "GSM T 410"}, + { 11, "GSM T 900"}, + { 12, "GSM 710"}, + { 13, "GSM T 810"}, + { 14, "reserved"}, + { 15, "Indicates the presence of a list of Additional access technologies"}, + { 0, NULL } +}; + + +static const value_string gsm_a_egprs_mcs_vals[] = { + { 0, "MCS-1"}, + { 1, "MCS-2"}, + { 2, "MCS-3"}, + { 3, "MCS-4"}, + { 4, "MCS-5"}, + { 5, "MCS-6"}, + { 6, "MCS-7"}, + { 7, "MCS-8"}, + { 8, "MCS-9"}, + { 9, "MCS-5-7"}, + { 10, "MCS-6-9"}, + { 11, "reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_gprs_cs_vals[] = { + { 0, "CS-1"}, + { 1, "CS-2"}, + { 2, "CS-3"}, + { 3, "CS-4"}, + { 0, NULL } +}; + + +static const true_false_string gsm_a_tlli_block_channel_coding_vals = { + "mobile station shall use coding scheme as specified by the corresponding CHANNEL CODING COMMAND or EGPRS CHANNEL CODING COMMAND field", + "mobile station shall use CS-1 in GPRS TBF mode or MCS-1 in EGPRS TBF mode" +}; + +static const true_false_string gsm_a_resegment_vals = { + "Retransmitted RLC data blocks shall be re-segmented according to commanded MCS", + "Retransmitted RLC data blocks shall not be re-segmented" +}; + +static const true_false_string gsm_a_polling_vals = { + "MS shall send a PACKET CONTROL ACKNOWLEDGEMENT message in the uplink block specified by TBF Starting Time, on the assigned PDCH", + "no action is required from MS" +}; + +static const true_false_string gsm_a_usf_granularity_vals = { + "the mobile station shall transmit four consecutive RLC/MAC blocks", + "the mobile station shall transmit one RLC/MAC block" +}; + +static const true_false_string gsm_a_rlc_mode_vals = { + "RLC unacknowledged mode", + "RLC acknowledged mode" +}; + +static const true_false_string gsm_a_ta_valid_vals = { + "the timing advance value is valid", + "the timing advance value is not valid" +}; + + + +static const value_string gsm_a_egprs_windows_size_vals[] = { + { 0, "64"}, + { 1, "96"}, + { 2, "128"}, + { 3, "160"}, + { 4, "192"}, + { 5, "224"}, + { 6, "256"}, + { 7, "288"}, + { 8, "320"}, + { 9, "352"}, + { 10, "384"}, + { 11, "416"}, + { 12, "448"}, + { 13, "480"}, + { 14, "512"}, + { 15, "544"}, + { 16, "576"}, + { 17, "608"}, + { 18, "640"}, + { 19, "672"}, + { 20, "704"}, + { 21, "736"}, + { 22, "768"}, + { 23, "800"}, + { 24, "832"}, + { 25, "864"}, + { 26, "896"}, + { 27, "928"}, + { 28, "960"}, + { 29, "992"}, + { 30, "1024"}, + { 31, "reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_gamma_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + { 10, "20 dB"}, + { 11, "22 dB"}, + { 12, "24 dB"}, + { 13, "26 dB"}, + { 14, "28 dB"}, + { 15, "30 dB"}, + { 16, "32 dB"}, + { 17, "34 dB"}, + { 18, "36 dB"}, + { 19, "38 dB"}, + { 20, "40 dB"}, + { 21, "42 dB"}, + { 22, "44 dB"}, + { 23, "46 dB"}, + { 24, "48 dB"}, + { 25, "50 dB"}, + { 26, "52 dB"}, + { 27, "54 dB"}, + { 28, "56 dB"}, + { 29, "58 dB"}, + { 30, "60 dB"}, + { 31, "62 dB"}, + { 0, NULL } +}; + + + +static const value_string gsm_a_link_quality_meas_mode_vals[] = { + { 0, "The MS shall not report either interference measurements or per slot BEP measurements"}, + { 1, "The MS shall report available interference measurements for timeslots 0 through 7"}, + { 2, "The MS shall report mean BEP on each assigned time slot ... No interference measurements shall be reported..."}, + { 3, "The MS shall report mean BEP on each assigned time slot ... In addition to mean BEP, the MS shall report interference measurements for no more than four time slots..."}, + { 0, NULL } +}; + + +static gint +de_rr_ia_rest_oct_egprs_packet_uplink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EGPRS_PACKET_UPLINK_ASSIGNMENT]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Access Technologies Request", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_access_tech_type, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "", "TFI Assignment Present", "Multi Block Allocation Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Allocation Type", "Fixed Allocation (not to be used after Rel-4)", "Dynamic Allocation (mandatory after Rel-4)")) + { + gint8 bitmap_len = tvb_get_bits8(tvb, curr_bit_offset, 5); + gint32 bitmap; + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+5)>>3) - (curr_bit_offset>>3), "Bitmap length %d", bitmap_len); + curr_bit_offset += 5; + bitmap = tvb_get_bits32(tvb, curr_bit_offset, bitmap_len, ENC_BIG_ENDIAN); + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+bitmap_len)>>3) - (curr_bit_offset>>3), "Bitmap %d", bitmap); + curr_bit_offset += bitmap_len; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_mcs, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli_block_channel_coding, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BEP_PERIOD2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bep_period2, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_resegment, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_window_size, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + } + else /* Multi Block Allocation */ + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_num_of_radio_block_allocated, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BTS Power Control Mode", "Mode B (not to be used after Rel-4)", "Mode A (mandatory after Rel-4)"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for Rel-7", "Present", "Not present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +static gint +de_rr_ia_rest_oct_tmgi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint16 value16; + gchar mcc[4]; + gchar mnc[4]; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_TMGI].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TMGI]); + + if (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MCC and MNC Parameters", "Present", "Not Present")) /* without MCC and MNC parameters */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mbms_service_id, tvb, curr_bit_offset, 24, ENC_BIG_ENDIAN); + curr_bit_offset += 24; + } + else /* with MCC and MNC parameters */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mbms_service_id, tvb, curr_bit_offset, 24, ENC_BIG_ENDIAN); + curr_bit_offset += 24; + + value16 = tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN); + mcc[0] = '0' + ((value16>>8)&0xf); + mcc[1] = '0' + ((value16>>4)&0xf); + mcc[2] = '0' + ((value16 )&0xf); + mcc[3] = '\0'; + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 12, + "Mobile Country Code (MCC): %s", + mcc); + curr_bit_offset += 12; + + value16 = tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN); + mnc[0] = '0' + ((value16>>8)&0xf); + mnc[1] = '0' + ((value16>>4)&0xf); + mnc[2] = '0' + ((value16 )&0xf); + mnc[3] = '\0'; + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 12, + "Mobile Network Code (MNC): %s", + mnc); + curr_bit_offset += 12; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + +static gint +de_rr_ia_rest_oct_packet_timing_advance(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_TIMING_ADVANCE]); + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Value", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index and Timeslot", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_timeslot_num, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + +static gint +de_rr_ia_rest_oct_multiple_blocks_packet_downlink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_MULTIPLE_BLOCKS_PACKET_DOWNLINK_ASSIGNMENT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_num_of_radio_block_allocated, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "", "default 0 bit", "Reserved Value")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MBMS Assignment", "(Non-distribution)", "(Distribution)")) + {/* MBMS Assignment (Distribution) */ + curr_bit_offset += de_rr_ia_rest_oct_tmgi(tvb, tree, curr_bit_offset); + } + else /* MBMS Assignment (Non-distribution) */ + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli, tvb, curr_bit_offset, 32, ENC_BIG_ENDIAN); + curr_bit_offset += 32; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MS Parameters", "Present", "Not Present")) + { + value = tvb_get_bits8(tvb,curr_bit_offset,2); + proto_tree_add_text(tree, + tvb, curr_bit_offset>>3, 2, + "Length Indicator of MS ID: %d", + value); + curr_bit_offset += 2; + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ms_id, tvb, curr_bit_offset, value+1, ENC_BIG_ENDIAN); + curr_bit_offset += value+1; + curr_bit_offset += de_rr_ia_rest_oct_packet_timing_advance(tvb, tree, curr_bit_offset); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Gamma", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + } + } + } + + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + + +static gint +de_rr_ia_rest_oct_packet_uplink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_UPLINK_ASSIGNMENT]); + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Packet Uplink Assignment", "Normal", "Single Block")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Allocation Type", "Fixed Allocation (not to be used after Rel-4))", "Dynamic Allocation (mandatory after Rel-4)")) + { + gint8 bitmap_len = tvb_get_bits8(tvb, curr_bit_offset, 5); + gint32 bitmap; + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+5)>>3) - (curr_bit_offset>>3), "Bitmap length %d", bitmap_len); + curr_bit_offset += 5; + bitmap = tvb_get_bits32(tvb, curr_bit_offset, bitmap_len, ENC_BIG_ENDIAN); + proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1+((curr_bit_offset+bitmap_len)>>3) - (curr_bit_offset>>3), "Bitmap %d", bitmap); + curr_bit_offset += bitmap_len; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0 bits", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_usf_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_cs, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli_block_channel_coding, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + } + else /* Single Block Allocation */ + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + /* fixed bits '01' */ + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Early R97 version", "Later than R97 version"); + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Later than R97 version", "Early R97 version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + /* The value '1' was allocated in an earlier version of the protocol and shall not be used */ + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for Rel-6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +static gint +de_rr_ia_rest_oct_packet_downlink_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PACKET_DOWNLINK_ASSIGNMENT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tlli, tvb, curr_bit_offset, 32, ENC_BIG_ENDIAN); + curr_bit_offset += 32; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TFI Assignment (etc)", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tfi_assignment, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rlc_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Alpha", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_alpha, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gamma, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_polling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ta_valid, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Timing Advance Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_timing_adv_index, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "TBF Starting Time", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tbf_starting_time, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "P0", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_p0, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BTS Power Control Mode", "Mode B (not to be used after Rel-4)", "Mode A (mandatory after Rel-4)"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pr_mode, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_egprs_window_size, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_link_quality_meas_mode, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BEP_PERIOD2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bep_period2, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + } + + if (gsm_rr_csn_HL_flag(tvb,subtree,0 ,curr_bit_offset++,"Additions for Rel-6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PFI", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_pfi, tvb, curr_bit_offset, 7, ENC_BIG_ENDIAN); + curr_bit_offset += 7; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + + + + +static gint +de_rr_ia_rest_oct_second_part_packet_assignment(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SECOND_PART_PACKET_ASSIGNMENT]); + + if (gsm_rr_csn_HL_flag(tvb,subtree, 0,curr_bit_offset++,"Additions for R99", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + + +/* + * [3] 10.5.2.16 IA Rest Octets + */ + +static guint16 +de_rr_ia_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + gint length; + guint64 ma_length; + guint8 tvb_len = tvb_length(tvb); + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_IA_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IA_REST_OCT]); + + bit_offset = curr_offset << 3; + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"first discriminator bit", "High", "Low") == FALSE) + { + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"second discriminator bit", "High", "Low") == FALSE) + { + /* LL */ + gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"A compressed version of the INTER RAT HANDOVER INFO message ", "shall be used", "shall not be used"); + } + else /* LH */ + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "reserved for future use", "EGPRS Packet Uplink Assignment or Multiple blocks Packet Downlink Assignment")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "", "Multiple blocks Packet Downlink Assignment", "EGPRS Packet Uplink Assignment")) + { + bit_offset += de_rr_ia_rest_oct_egprs_packet_uplink_assignment(tvb, tree, bit_offset); + } + else + { + bit_offset += de_rr_ia_rest_oct_multiple_blocks_packet_downlink_assignment(tvb, tree, bit_offset); + } + } + else + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "reserved for future use (however the value 7C for the first octet shall not be used)"); + } + } + } + else + { + if (0 == gsm_rr_csn_HL_flag(tvb,subtree, 0,bit_offset++,"second discriminator bit:", "High", "Low")) + { + /* HL */ + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_ma_length, tvb, bit_offset, 6, &ma_length, ENC_BIG_ENDIAN); + bit_offset += 6; + /* Frequency Parameters, before time */ + if (ma_length>0) + { + /* two '0' bits */ + bit_offset += 2; + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "MAIO: %d", tvb_get_bits8(tvb,bit_offset,6)); + bit_offset += 6; + length = (gint)ma_length; + item = proto_tree_add_text(subtree,tvb, bit_offset>>3, (length>>3)-1, "MA Bitmap: "); + length = (length-1)*8; + while (length) + { + proto_item_append_text(item,"%d",tvb_get_bits8(tvb,bit_offset,1)); + bit_offset += 1; + length -= 1; + } + } + gsm_rr_csn_HL_flag(tvb,subtree, 0,bit_offset++,"A compressed version of the INTER RAT HANDOVER INFO message", "shall be used", "shall not be used"); + } + else /* HH */ + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "Second Part Packet Assignment", "Packet Uplik Assignment or Packet Downlink Assignment")) + { + if (0 == gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Discriminator bit:", "Packet Downlink Assignment", "Packet Uplink Assignment")) + { + /* 00 < Packet Uplink Assignment > */ + bit_offset += de_rr_ia_rest_oct_packet_uplink_assignment(tvb, tree, bit_offset); + } + else /* 01 < Packet Downlink Assignment > */ + { + bit_offset += de_rr_ia_rest_oct_packet_downlink_assignment(tvb, tree, bit_offset); + } + } + else /* 1 < Second Part Packet Assignment > */ + { + bit_offset += de_rr_ia_rest_oct_second_part_packet_assignment(tvb, tree, bit_offset); + } + } + } + + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.17 IAR Rest Octets + */ + +static guint16 +de_rr_iar_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_bit_offset; + guint8 i; + guint8 tvb_len = tvb_length(tvb); + + curr_bit_offset = offset<<3; + + item = + proto_tree_add_text(tree, + tvb, offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_IAR_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IAR_REST_OCT]); + + for( i=0; i<4; i++ ) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Extended RA", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "Extended RA %d present", i); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_extended_ra, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + else + { + proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "Extended RA %d not present", i); + } + } + gsm_rr_csn_spare_bits(tree, tvb, curr_bit_offset); + return tvb_len - offset; +} + +/* + * [3] 10.5.2.18 IAX Rest Octets + */ +static guint16 +de_rr_iax_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_bit_offset; + guint8 tvb_len = tvb_length(tvb); + + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_bit_offset = offset<<3; + + item = + proto_tree_add_text(tree, + tvb, offset, len, "%s", + gsm_rr_elem_strings[DE_RR_IAX_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_IAX_REST_OCT]); + + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "A compressed version of the INTER RAT HANDOVER INFO message", "shall be used", "shall not be used"); + + gsm_rr_csn_spare_bits(tree, tvb, curr_bit_offset); + return tvb_len - offset; +} + +/* + * [3] 10.5.2.19 L2 Pseudo Length + */ +static guint16 +de_rr_l2_pseudo_len(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 1, "%s", gsm_rr_elem_strings[DE_RR_L2_PSEUDO_LEN].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_L2_PSEUDO_LEN]); + + /* L2 Pseudo Length value */ + proto_tree_add_item(subtree, hf_gsm_a_rr_L2_pseudo_len, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.20 Measurement Results + */ +static const true_false_string gsm_a_rr_dtx_vals = { + "DTX was used", + "DTX was not used" +}; + + +static const true_false_string gsm_a_rr_mv_vals = { + "The measurement results are not valid", + "The measurement results are valid" +}; + +static const value_string gsm_a_rr_rxqual_vals [] = { + {0, "BER < 0.2%, Mean value 0.14%"}, + {1, "0.2% <= BER < 0.4%, Mean value 0.28%"}, + {2, "0.4% <= BER < 0.8%, Mean value 0.57%"}, + {3, "0.8% <= BER < 1.6%, Mean value 1.13%"}, + {4, "1.6% <= BER < 3.2%, Mean value 2.26%"}, + {5, "3.2% <= BER < 6.4%, Mean value 4.53%"}, + {6, "6.4% <= BER < 12.8%, Mean value 9.05%"}, + {7, "BER > 12.8%, Mean value 18.10%"}, + {0, NULL} +}; + +static const value_string gsm_a_rr_ncell_vals [] = { + {0, "No neighbour cell measurement result"}, + {1, "1 neighbour cell measurement result"}, + {2, "2 neighbour cell measurement result"}, + {3, "3 neighbour cell measurement result"}, + {4, "4 neighbour cell measurement result"}, + {5, "5 neighbour cell measurement result"}, + {6, "6 neighbour cell measurement result"}, + {7, "Neighbour cell information not available for serving cell"}, + {0, NULL} +}; + +guint16 +de_rr_meas_res(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + guint64 no_ncell_m; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 16, "%s", + gsm_rr_elem_strings[DE_RR_MEAS_RES].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_MEAS_RES]); + + /* 2nd octet */ + /* BA-USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ba_used, tvb, curr_offset<<3, 1, ENC_BIG_ENDIAN); + /* DTX USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dtx_used, tvb, (curr_offset<<3)+1, 1, ENC_BIG_ENDIAN); + /* RXLEV-FULL-SERVING-CELL */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_full_serv_cell, tvb, (curr_offset<<3)+2, 6, ENC_BIG_ENDIAN); + curr_offset++; + + /* 3rd octet */ + /* 3G-BA-USED */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ba_used, tvb, curr_offset<<3, 1, ENC_BIG_ENDIAN); + /* MEAS-VALID */ + proto_tree_add_item(subtree, hf_gsm_a_rr_meas_valid, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* RXLEV-SUB-SERVING-CELL */ + proto_tree_add_item(subtree, hf_gsm_a_rr_rxlev_sub_serv_cell, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset++; + + /* 4th octet */ + /* RXQUAL-FULL-SERVING-CELL */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxqual_full_serv_cell, tvb, (curr_offset<<3)+1, 3, ENC_BIG_ENDIAN); + + /* RXQUAL-SUB-SERVING-CELL */ + proto_tree_add_item(subtree, hf_gsm_a_rr_rxqual_sub_serv_cell, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* NO-NCELL-M */ + bit_offset = (curr_offset << 3) + 7; + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_no_ncell_m, tvb, bit_offset, 3, &no_ncell_m, ENC_BIG_ENDIAN); + bit_offset += 3; + if (no_ncell_m == 7) /* No neighbour cell information available) */ + no_ncell_m = 0; + while (no_ncell_m) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_freq_ncell, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + no_ncell_m -= 1; + } + + return(len); +} + +/* + * [3] 10.5.2.20a GPRS Measurement Results + */ +/* + * [3] 10.5.2.21 Mobile Allocation + */ +static guint16 +de_rr_mob_all(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + proto_item *item; + gint i, j; + guint8 value; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "Bitmap of increasing ARFCNs included in the Mobile Allocation: "); + for(i=len; i>0; i--) + { + value = tvb_get_guint8(tvb,curr_offset+i-1); + for (j=0; j<8; j++) + { + proto_item_append_text(item,"%d",(value>>j)&0x01); + } + } + + curr_offset = curr_offset + len; + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.21a Mobile Time Difference + */ +static guint16 +de_rr_mob_time_diff(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_mobile_time_difference, tvb, curr_offset, len, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + len; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21aa MultiRate configuration + */ +/* Multirate speech version Octet 3 Bits 8 7 6 */ +static const value_string multirate_speech_ver_vals[] = { + { 1, "Adaptive Multirate speech version 1"}, + { 2, "Adaptive Multirate speech version 2"}, + { 0, NULL } +}; +/* Bit 5 NSCB: Noise Suppression Control Bit */ +static const value_string NSCB_vals[] = { + { 0, "Noise Suppression can be used (default)"}, + { 1, "Noise Suppression shall be turned off"}, + { 0, NULL } +}; +/* Bit 4 ICMI: Initial Codec Mode Indicator */ +static const value_string ICMI_vals[] = { + { 0, "The initial codec mode is defined by the implicit rule provided in 3GPP TS 05.09"}, + { 1, "The initial codec mode is defined by the Start Mode field"}, + { 0, NULL } +}; +/* +Table 10.5.2.21aa.2: Set of adaptive multirate codec modes field (octet 4) +for the Multirate speech version 1 +*/ +static const true_false_string gsm_a_rr_set_of_amr_codec_modes = { + "is part of the subset", + "is not part of the subset" +}; + +static const value_string gsm_a_rr_amr_threshold_vals[] = { + { 0, "0.0 dB"}, + { 1, "0.5 dB"}, + { 2, "1.0 dB"}, + { 3, "1.5 dB"}, + { 4, "2.0 dB"}, + { 5, "2.5 dB"}, + { 6, "3.0 dB"}, + { 7, "3.5 dB"}, + { 8, "4.0 dB"}, + { 9, "4.5 dB"}, + { 10, "5.0 dB"}, + { 11, "5.5 dB"}, + { 12, "6.0 dB"}, + { 13, "6.5 dB"}, + { 14, "7.0 dB"}, + { 15, "7.5 dB"}, + { 16, "8.0 dB"}, + { 17, "8.5 dB"}, + { 18, "9.0 dB"}, + { 19, "9.5 dB"}, + { 20, "10.0 dB"}, + { 21, "10.5 dB"}, + { 22, "11.0 dB"}, + { 23, "11.5 dB"}, + { 24, "12.0 dB"}, + { 25, "12.5 dB"}, + { 26, "13.0 dB"}, + { 27, "13.5 dB"}, + { 28, "14.0 dB"}, + { 29, "14.5 dB"}, + { 30, "15.0 dB"}, + { 31, "15.5 dB"}, + { 32, "16.0 dB"}, + { 33, "16.5 dB"}, + { 34, "17.0 dB"}, + { 35, "17.5 dB"}, + { 36, "18.0 dB"}, + { 37, "18.5 dB"}, + { 38, "19.0 dB"}, + { 39, "19.5 dB"}, + { 40, "20.0 dB"}, + { 41, "20.5 dB"}, + { 42, "21.0 dB"}, + { 43, "21.5 dB"}, + { 44, "22.0 dB"}, + { 45, "22.5 dB"}, + { 46, "23.0 dB"}, + { 47, "23.5 dB"}, + { 48, "24.0 dB"}, + { 49, "24.5 dB"}, + { 50, "25.0 dB"}, + { 51, "25.5 dB"}, + { 52, "26.0 dB"}, + { 53, "26.5 dB"}, + { 54, "27.0 dB"}, + { 55, "27.5 dB"}, + { 56, "28.0 dB"}, + { 57, "28.5 dB"}, + { 58, "29.0 dB"}, + { 59, "29.5 dB"}, + { 60, "30.0 dB"}, + { 61, "30.5 dB"}, + { 62, "31.0 dB"}, + { 63, "31.5 dB"}, + { 0, NULL } +}; + +static value_string_ext gsm_a_rr_amr_threshold_vals_ext = VALUE_STRING_EXT_INIT(gsm_a_rr_amr_threshold_vals); + +static const value_string gsm_a_rr_amr_hysteresis_vals[] = { + { 0, "0.0 dB"}, + { 1, "0.5 dB"}, + { 2, "1.0 dB"}, + { 3, "1.5 dB"}, + { 4, "2.0 dB"}, + { 5, "2.5 dB"}, + { 6, "3.0 dB"}, + { 7, "3.5 dB"}, + { 8, "4.0 dB"}, + { 9, "4.5 dB"}, + { 10, "5.0 dB"}, + { 11, "5.5 dB"}, + { 12, "6.0 dB"}, + { 13, "6.5 dB"}, + { 14, "7.0 dB"}, + { 15, "7.5 dB"}, + { 0, NULL } +}; + +guint16 +de_rr_multirate_conf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct; + gint bit_offset, remaining_length, nb_of_params; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_multirate_speech_ver, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_NCSB, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_ICMI, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /* The initial codec mode is coded as in 3GPP TS 45.009 */ + proto_tree_add_item(tree, hf_gsm_a_rr_start_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + oct = ( tvb_get_guint8(tvb,curr_offset) &0xe0 ) >> 5; + curr_offset++; + switch ( oct){ + case 1: + /* Adaptive Multirate speech version 1 */ + /* Set of AMR codec modes */ + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + remaining_length = len-2; + break; + case 2: + /* Adaptive Multirate speech version 2 */ + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + remaining_length = len-2; + break; + default: + proto_tree_add_text(tree,tvb,offset,1,"Unknown version"); + proto_tree_add_text(tree,tvb, curr_offset, len-1 ,"Data(Not decoded)"); + remaining_length = 0; + break; + } + + if (remaining_length) + { + bit_offset = (curr_offset<<3) + 2; + nb_of_params = remaining_length - 1; + while (nb_of_params) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_amr_threshold, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_amr_hysteresis, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + nb_of_params -= 1; + } + } + + curr_offset = offset + len; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21b Multislot Allocation + */ +static guint16 +de_rr_mult_all(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_item *item; + guint32 curr_offset; + guint8 oct; + guint8 i; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb, curr_offset, 1 ,"List of DA:"); + + oct = tvb_get_guint8(tvb, curr_offset); + curr_offset++; + for( i=0;i<7;i++ ) + { + if( (oct>>i) & 1 ) + { + proto_item_append_text(item," DA%d",i+1); + } + } + + if( oct & 0x80 ) /* octet 3a present */ + { + item = proto_tree_add_text(tree,tvb, curr_offset, 1 ,"List of UA:"); + oct = tvb_get_guint8(tvb, curr_offset); + curr_offset++; + for( i=0;i<7;i++ ) + { + if( (oct>>i) & 1 ) + { + proto_item_append_text(item," UA%d",i+1); + } + } + } + + while ( curr_offset < offset + len ) + { + proto_tree_add_item(tree, hf_gsm_a_rr_ma_channel_set, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + } + + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.21c NC mode + */ + + /* + * [3] 10.5.2.22 Neighbour Cell Description + */ +static const value_string gsm_a_rr_ext_ind_vals[] = { + { 0, "The information element carries the complete BA"}, + { 1, "The information element carries only a part of the BA"}, + { 0, NULL } +}; +static guint16 +de_rr_neigh_cell_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_ext_ind, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, (curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + + /* + * [3] 10.5.2.22a Neighbour Cell Description 2 + */ +static guint16 +de_rr_neigh_cell_desc2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_multiband_reporting, tvb, (curr_offset<<3)+1, 2, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, (curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list2(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets + */ + +/* + * [3] 10.5.2.23 P1 Rest Octets + */ +static guint16 +de_rr_p1_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset, value; + gint bit_offset, bit_offset_sav; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = tvb_length_remaining(tvb,offset); + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P1_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P1_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "NLN(PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Group Call Information", "Present", "Not present")) + { /* Group Call Information */ + bit_offset_sav = bit_offset; + bit_offset += 36; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Group Channel Description", "Present", "Not Present")) + { /* Group Channel Description */ + bit_offset += 24; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Hopping case", "Present", "Not Present")) + { /* Hopping case */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "????", "Present", "Not Present")) + { + bit_offset += 64; + } + else + { + value = tvb_get_bits8(tvb,bit_offset,8); + bit_offset += 8 + (value<<3); + } + } + } + proto_tree_add_text(subtree,tvb, bit_offset_sav>>3, (bit_offset-bit_offset_sav)>>3,"Group Call Information: Data(Not decoded)"); + } + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Page Indication 1", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Page Indication 2", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = curr_offset + len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.24 P2 Rest Octets + */ +static guint16 +de_rr_p2_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) + +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = tvb_length_remaining(tvb,offset); + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P2_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P2_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Channel Needed 3", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len ,bit_offset++, "NLN (PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 3", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 3"); + } + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 1", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 2", "For GPRS", "For RR connection establishment"); + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Packet Paging Procedure 3", "For GPRS", "For RR connection establishment"); + + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Additions in release 6", "Present", "Not present")) + { /* Additions in release 6 */ + bit_offset += 1; + proto_tree_add_text(subtree, tvb, bit_offset>>3, -1,"Additions in Release 6: Data(Not decoded)"); + } + else + bit_offset += 1; + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = curr_offset + len; + + return (curr_offset - offset); +} + +/* + * [3] 10.5.2.25 P3 Rest Octets + */ +static guint16 +de_rr_p3_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset; + guint8 tvb_len = tvb_length(tvb); + + curr_offset = offset; + bit_offset = curr_offset << 3; + len = 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_P3_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_P3_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Channel Needed 3 & 4", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_chnl_needed_ch4, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "NLN (PCH)", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_pch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nln_status_pch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 1", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 1"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 2", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 2"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 3", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 3"); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Priority 4", "Present", "Not present")) + { + item2 = proto_tree_add_bits_item(subtree, hf_gsm_a_call_prio, tvb, bit_offset, 3, FALSE); + bit_offset += 3; + proto_item_append_text(item2, " for Mobile Identity 4"); + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.25a Packet Channel Description C V 3 + */ +static guint16 +de_rr_packet_ch_desc(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + guint8 oct8; + guint16 arfcn, hsn, maio; + proto_tree *subtree; + proto_item *item; + const gchar *str; + + curr_offset = offset; + + item = proto_tree_add_text(tree,tvb,curr_offset,3, "%s", gsm_rr_elem_strings[DE_RR_PACKET_CH_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_PACKET_CH_DESC]); + + /* Octet 2 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + /* Channel Type */ + str = "Spare bits (ignored by receiver)"; + other_decode_bitfield_value(a_bigbuf, oct8, 0xf8, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = %s",a_bigbuf,str); + /* TN */ + other_decode_bitfield_value(a_bigbuf, oct8, 0x07, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Timeslot: %d",a_bigbuf,(oct8 & 0x07)); + + curr_offset +=1; + + /* Octet 3 */ + oct8 = tvb_get_guint8(tvb, curr_offset); + other_decode_bitfield_value(a_bigbuf, oct8, 0xe0, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Training Sequence: %d",a_bigbuf,((oct8 & 0xe0)>>5)); + + if ((oct8 & 0x10) == 0x10) + { + /* Hopping sequence */ + maio = ((oct8 & 0x0f)<<2) | ((tvb_get_guint8(tvb,curr_offset+1) & 0xc0) >> 6); + hsn = (tvb_get_guint8(tvb,curr_offset+1) & 0x3f); + str = "Yes"; + + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: MAIO %d",maio); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Hopping channel: HSN %d",hsn); + } + else + { + /* single ARFCN */ + arfcn = ((oct8 & 0x03) << 8) | tvb_get_guint8(tvb,curr_offset+1); + str = "No"; + other_decode_bitfield_value(a_bigbuf, oct8, 0x10, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Hopping channel: %s",a_bigbuf,str); + other_decode_bitfield_value(a_bigbuf, oct8, 0x0c, 8); + proto_tree_add_text(subtree,tvb, curr_offset, 1,"%s = Spare",a_bigbuf); + proto_tree_add_text(subtree,tvb, curr_offset, 2,"Single channel : ARFCN %d",arfcn); + } + + curr_offset = curr_offset + 2; + return(curr_offset - offset); + +} +/* + * [3] 10.5.2.25b Dedicated mode or TBF + */ + +static const value_string gsm_a_rr_dedicated_mode_or_tbf_vals[] = { + { 0, "This message assigns a dedicated mode resource"}, + { 1, "This message assigns an uplink TBF or is the second message of two in a two-message assignment of an uplink or downlink TBF"}, + { 2, "Not used"}, + { 3, "This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE"}, + { 4, "Not used"}, + { 5, "This message is the first message of two in a two-message assignment of an uplink TBF"}, + { 6, "Not used"}, + { 7, "This message is the first message of two in a two-message assignment of a downlink TBF to the mobile station identified in the IA Rest Octets IE"}, + { 0, NULL } +}; +static guint16 +de_rr_ded_mod_or_tbf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_DED_MOD_OR_TBF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_DED_MOD_OR_TBF]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_dedicated_mode_or_tbf, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment + */ +/* + * [3] 10.5.2.26 Page Mode + */ + +static const value_string gsm_a_rr_page_mode_vals[] = { + { 0, "Normal paging"}, + { 1, "Extended paging"}, + { 2, "Paging reorganization"}, + { 3, "Same as before"}, + { 0, NULL } +}; +static guint16 +de_rr_page_mode(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_PAGE_MODE].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_PAGE_MODE]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_page_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ +/* + * [3] 10.5.2.27 NCC Permitted + */ +static guint16 +de_rr_ncc_perm(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_NCC_PERM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_NCC_PERM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_ncc_permitted, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.28 Power Command + * + * + * ATC (Access Type Control) (octet 2)Bit 8 + * 0 Sending of Handover access is mandatory + * 1 Sending of Handover access is optional + */ +static const true_false_string gsm_a_rr_pow_cmd_atc_value = { + "Sending of Handover access is optional", + "Sending of Handover access is mandatory" +}; +/* + * The EPC mode field (octet 2) indicates whether the assigned channel(s) + * shall be in enhanced power control (EPC) mode. It is only valid for channels + * on which EPC may be used. It is coded as follows: +*/ +static const true_false_string gsm_a_rr_pow_cmd_epc_value = { + "Channel(s) in EPC mode", + "Channel(s) not in EPC mode" +}; +/* + * FPC_EPC (octet 2) + * The FPC_EPC field (octet 2) has different interpretation depending + * on the channel mode of the assigned channel (s) and the value + * of the EPC mode field. + * If the channel mode is such that fast power control (FPC) may be + * used, the FPC_EPC field indicates whether Fast Measurement + * Reporting and Power Control mechanism is used. + * It is coded as follows: + * Value 0 FPC not in use + * 1 FPC in use + * If the channel mode is such that EPC may be used and the EPC mode + * field indicates that the channel is in EPC mode, the FPC_EPC + * field indicates whether EPC shall be used for uplink power control. + * It is coded as follows: + * Value 0 EPC not in use for uplink power control + * 1 EPC in use for uplink power control + * + */ +static const true_false_string gsm_a_rr_pow_cmd_fpcepc_value = { + "FPC in use/EPC in use for uplink power control", + "FPC not in use/C not in use for uplink power control" +}; + +/* + * Power level (octet 2)The power level field is coded as the binaryRepresentation + * of the "power control level", see 3GPP TS 3GPP TS 45.005. This value shall be used + * by the mobile station According to 3GPP TS 45.008.Range: 0 to 31. + */ + +static guint16 +de_rr_pow_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_POW_CMD].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_POW_CMD]); + + proto_tree_add_item(subtree, hf_gsm_a_b8spare, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*EPC mode */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_epc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*FPC_EPC*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_fpcepc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*POWER LEVEL*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_powlev, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.28a Power Command and access type + */ +static guint16 +de_rr_pow_cmd_and_acc_type(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_rr_elem_strings[DE_RR_POW_CMD_AND_ACC_TYPE].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_POW_CMD_AND_ACC_TYPE]); + + /*ATC */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_atc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*EPC mode */ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_epc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*FPC_EPC*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_fpcepc, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*POWER LEVEL*/ + proto_tree_add_item(subtree, hf_gsm_a_rr_pow_cmd_powlev, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.29 RACH Control Parameters + */ + +static const value_string gsm_a_rr_max_retrans_vals[] = { + { 0, "Maximum 1 retransmission"}, + { 1, "Maximum 2 retransmissions"}, + { 2, "Maximum 4 retransmissions"}, + { 3, "Maximum 7 retransmissions"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_tx_integer_vals[] = { + { 0, "3 slots used to spread transmission"}, + { 1, "4 slots used to spread transmission"}, + { 2, "5 slots used to spread transmission"}, + { 3, "6 slots used to spread transmission"}, + { 4, "7 slots used to spread transmission"}, + { 5, "8 slots used to spread transmission"}, + { 6, "9 slots used to spread transmission"}, + { 7, "10 slots used to spread transmission"}, + { 8, "11 slots used to spread transmission"}, + { 9, "12 slots used to spread transmission"}, + { 10, "14 slots used to spread transmission"}, + { 11, "16 slots used to spread transmission"}, + { 12, "20 slots used to spread transmission"}, + { 13, "25 slots used to spread transmission"}, + { 14, "32 slots used to spread transmission"}, + { 15, "50 slots used to spread transmission"}, + { 0, NULL } +}; +static const value_string gsm_a_rr_cell_barr_access_vals[] = { + { 0, "The cell is not barred"}, + { 1, "The cell is barred"}, + { 0, NULL } +}; +static const value_string gsm_a_rr_re_vals[] = { + { 0, "Call Reestablishment allowed in the cell"}, + { 1, "Call Reestablishment not allowed in the cell"}, + { 0, NULL } +}; + +static guint16 +de_rr_rach_ctrl_param(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_RACH_CTRL_PARAM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_RACH_CTRL_PARAM]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_max_retrans, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_tx_integer, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_cell_barr_access, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_re, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + proto_tree_add_item(subtree, hf_gsm_a_rr_acc, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.30 Request Reference M V 3 + */ +static guint16 reduced_frame_number(guint16 fn) +{ + /* great care needed with signed/unsigned - -1 in unsigned is 0xffff, which mod(26) is not what you think !!! */ + gint16 t2, t3, t; + guint16 frame, t1; + + t1 = (fn >> 11) & 0x1f; + t2 = (fn >> 0) & 0x1f; + t3 = (fn >> 5) & 0x3f; + + t = (t3-t2)%26; + if (t<0) + t += 26; + + frame = 51*(unsigned)t+(unsigned)t3+51*26*t1; + + return frame; +} + +static guint16 +de_rr_req_ref(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + guint16 rfn; + guint16 fn; + + curr_offset = offset; + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_REQ_REF].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_REQ_REF]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_ra, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + fn = tvb_get_ntohs(tvb,curr_offset); + rfn = reduced_frame_number(fn); + proto_tree_add_item(subtree, hf_gsm_a_rr_T1prim, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(subtree, hf_gsm_a_rr_T3, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + curr_offset++; + proto_tree_add_item(subtree, hf_gsm_a_rr_T2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + item = proto_tree_add_uint(subtree, hf_gsm_a_rr_rfn, tvb, curr_offset-2, 2, rfn); + PROTO_ITEM_SET_GENERATED(item); + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.31 + */ +guint16 +de_rr_cause(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_RR_cause, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset++; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.32 SI 1 Rest Octets + */ +static const value_string gsm_a_rr_nch_position_vals[] = { + { 0, "No of blocks = 1 and Number of first block = 0"}, + { 1, "No of blocks = 1 and Number of first block = 1"}, + { 2, "No of blocks = 1 and Number of first block = 2"}, + { 3, "No of blocks = 1 and Number of first block = 3"}, + { 4, "No of blocks = 1 and Number of first block = 4"}, + { 5, "No of blocks = 1 and Number of first block = 5"}, + { 6, "No of blocks = 1 and Number of first block = 6"}, + { 7, "No of blocks = 1 and Number of first block = 0"}, + { 8, "No of blocks = 2 and Number of first block = 1"}, + { 9, "No of blocks = 2 and Number of first block = 2"}, + {10, "No of blocks = 2 and Number of first block = 3"}, + {11, "No of blocks = 2 and Number of first block = 4"}, + {12, "No of blocks = 2 and Number of first block = 5"}, + {13, "No of blocks = 3 and Number of first block = 0"}, + {14, "No of blocks = 3 and Number of first block = 1"}, + {15, "No of blocks = 3 and Number of first block = 2"}, + {16, "No of blocks = 3 and Number of first block = 3"}, + {17, "No of blocks = 3 and Number of first block = 4"}, + {18, "No of blocks = 4 and Number of first block = 0"}, + {19, "No of blocks = 4 and Number of first block = 1"}, + {20, "No of blocks = 4 and Number of first block = 2"}, + {21, "No of blocks = 4 and Number of first block = 3"}, + {22, "No of blocks = 5 and Number of first block = 0"}, + {23, "No of blocks = 5 and Number of first block = 1"}, + {24, "No of blocks = 5 and Number of first block = 2"}, + {25, "No of blocks = 6 and Number of first block = 0"}, + {26, "No of blocks = 6 and Number of first block = 1"}, + {27, "No of blocks = 7 and Number of first block = 0"}, + {28, "Reserved"}, + {29, "Reserved"}, + {30, "Reserved"}, + {31, "Reserved"}, + { 0, NULL } +}; + +static guint16 +de_rr_si1_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + + len = 1; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI1_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI1_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "NCH position", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nch_position, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Band Indicator", "1900", "1800"); + + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.33 SI 2bis Rest Octets + */ + +/* + * [3] 10.5.2.33a SI 2ter Rest Octets + */ +static const value_string gsm_a_rr_qsearch_x_vals[] = { + { 0, "-98 dBm"}, + { 1, "-94 dBm"}, + { 2, "-90 dBm"}, + { 3, "-86 dBm"}, + { 4, "-82 dBm"}, + { 5, "-78 dBm"}, + { 6, "-74 dBm"}, + { 7, "Always"}, + { 8, "-78 dBm"}, + { 9, "-74 dBm"}, + {10, "-70 dBm"}, + {11, "-66 dBm"}, + {12, "-62 dBm"}, + {13, "-58 dBm"}, + {14, "-54 dBm"}, + {15, "Never"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_xdd_qoffset_vals[] = { + { 0, "always select a cell if acceptable"}, + { 1, "-28 dB"}, + { 2, "-24 dB"}, + { 3, "-20 dB"}, + { 4, "-16 dB"}, + { 5, "-12 dB"}, + { 6, "-8 dB"}, + { 7, "-4 dB"}, + { 8, "0 dB"}, + { 9, "4 dB"}, + {10, "8 dB"}, + {11, "12 dB"}, + {12, "16 dB"}, + {13, "20 dB"}, + {14, "24 dB"}, + {15, "28 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_qmin_vals[] = { + { 0, "-20 dB"}, + { 1, "-6 dB"}, + { 2, "-18 dB"}, + { 3, "-8 dB"}, + { 4, "-16 dB"}, + { 5, "-10 dB"}, + { 6, "-14 dB"}, + { 7, "-12 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_qmin_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_fdd_rscpmin_vals[] = { + { 0, "-114 dBm"}, + { 1, "-112 dBm"}, + { 2, "-110 dBm"}, + { 3, "-108 dBm"}, + { 4, "-106 dBm"}, + { 5, "-104 dBm"}, + { 6, "-102 dBm"}, + { 7, "-100 dBm"}, + { 8, "-98 dBm"}, + { 9, "-96 dBm"}, + {10, "-94 dBm"}, + {11, "-92 dBm"}, + {12, "-90 dBm"}, + {13, "-88 dBm"}, + {14, "-86 dBm"}, + {15, "-84 dBm"}, + { 0, NULL } +}; + +static guint16 +de_rr_si2ter_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + + len = 4; + curr_offset = offset; + bit_offset = curr_offset<<3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI2TER_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI2TER_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "MP Changemark", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_3g_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_index, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2ter_count, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Current version", "Earlier version"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bandwidth_fdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bit reserved for earlier version of protocol:", "Current version", "Earlier version"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_uarfcn, tvb, bit_offset, 14, ENC_BIG_ENDIAN); + bit_offset += 14; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_bandwidth_tdd, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_i, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-5", "Present", "Not present")) + { /* Additions in release R5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rscpmin, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.33b SI 2quater Rest Octets + */ +static const true_false_string gsm_a_rr_gsm_report_type_value = { + "The MS shall use the Measurement Report message for reporting", + "The MS shall use the Enhanced Measurement Report message for reporting if at least one BSIC is allocated to each BA (list) frequency. Otherwise, the Measurement Report message shall be used" +}; + +static const true_false_string gsm_a_rr_frequency_scrolling_value = { + "Next BSIC in the structure relates to the subsequent frequency in the BA(list)", + "Next BSIC in the structure relates to the same frequency in the BA(list)" +}; + +static const true_false_string gsm_a_rr_rep_priority_value = { + "High reporting priority", + "Normal reporting priority" +}; + +static const true_false_string gsm_a_rr_report_type_value = { + "The MS shall use the PACKET MEASUREMENT REPORT message for reporting", + "The MS shall use the PACKET ENHANCED MEASUREMENT REPORT message for reporting" +}; + +static const true_false_string gsm_a_rr_reporting_rate_value = { + "Reduced reporting rate allowed", + "Normal reporting rate" +}; + +static const true_false_string gsm_a_rr_invalid_bsic_reporting_value = { + "Report on cells with invalid BSIC and allowed NCC part of BSIC is allowed", + "Report on cells with invalid BSIC and allowed NCC part of BSIC is not allowed" +}; + +static const value_string gsm_a_rr_scale_ord_vals[] = { + { 0, "An offset of 0 dB shall be used for the reported RXLEV values"}, + { 1, "An offset of 10 dB shall be used for the reported RXLEV values"}, + { 2, "An automatic offset shall be used for the reported RXLEV values"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_xxx_reporting_offset_vals[] = { + { 0, "Apply an offset of 0 dB to the reported value when prioritising the cells for reporting"}, + { 1, "Apply an offset of 6 dB to the reported value when prioritising the cells for reporting"}, + { 2, "Apply an offset of 12 dB to the reported value when prioritising the cells for reporting"}, + { 3, "Apply an offset of 18 dB to the reported value when prioritising the cells for reporting"}, + { 4, "Apply an offset of 24 dB to the reported value when prioritising the cells for reporting"}, + { 5, "Apply an offset of 30 dB to the reported value when prioritising the cells for reporting"}, + { 6, "Apply an offset of 36 dB to the reported value when prioritising the cells for reporting"}, + { 7, "Apply an offset of 42 dB to the reported value when prioritising the cells for reporting"}, + { 0, NULL } +}; + +/* The TS 45.008 is not that clear, but it should be understood: threshold = minimum reported value + 6 * resolution + * The minimum reported value, resolution is, for the different access technologies: + * GSM: -111 dBm, 1 dBm + * WCDMA/RSCP: -116 dBm, 1 dBm + * WCDMA/EcN0: -24.5 dB, 0.5 dB + * LTE/RSRP: -140 dBm, 1 dB + * LTE/RSRQ: -19.5 dB, 0.5 dB + */ +static const value_string gsm_a_rr_gsm_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -111 dBm"}, + { 1, "Apply priority reporting if the reported value is above -105 dBm"}, + { 2, "Apply priority reporting if the reported value is above -99 dBm"}, + { 3, "Apply priority reporting if the reported value is above -93 dBm"}, + { 4, "Apply priority reporting if the reported value is above -87 dBm"}, + { 5, "Apply priority reporting if the reported value is above -81 dBm"}, + { 6, "Apply priority reporting if the reported value is above -75 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_wcdma_rscp_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -116 dBm"}, + { 1, "Apply priority reporting if the reported value is above -110 dBm"}, + { 2, "Apply priority reporting if the reported value is above -104 dBm"}, + { 3, "Apply priority reporting if the reported value is above -98 dBm"}, + { 4, "Apply priority reporting if the reported value is above -92 dBm"}, + { 5, "Apply priority reporting if the reported value is above -86 dBm"}, + { 6, "Apply priority reporting if the reported value is above -80 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_wcdma_ecn0_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -24.5 dB"}, + { 1, "Apply priority reporting if the reported value is above -21.5 dB"}, + { 2, "Apply priority reporting if the reported value is above -17.5 dB"}, + { 3, "Apply priority reporting if the reported value is above -14.5 dB"}, + { 4, "Apply priority reporting if the reported value is above -11.5 dB"}, + { 5, "Apply priority reporting if the reported value is above -8.5 dB"}, + { 6, "Apply priority reporting if the reported value is above -5.5 dB"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lte_rsrp_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -140 dBm"}, + { 1, "Apply priority reporting if the reported value is above -134 dBm"}, + { 2, "Apply priority reporting if the reported value is above -128 dBm"}, + { 3, "Apply priority reporting if the reported value is above -122 dBm"}, + { 4, "Apply priority reporting if the reported value is above -116 dBm"}, + { 5, "Apply priority reporting if the reported value is above -110 dBm"}, + { 6, "Apply priority reporting if the reported value is above -104 dBm"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lte_rsrq_reporting_threshold_vals[] = { + { 0, "Apply priority reporting if the reported value is above -19.5 dB"}, + { 1, "Apply priority reporting if the reported value is above -16.5 dB"}, + { 2, "Apply priority reporting if the reported value is above -13.5 dB"}, + { 3, "Apply priority reporting if the reported value is above -10.5 dB"}, + { 4, "Apply priority reporting if the reported value is above -7.5 dB"}, + { 5, "Apply priority reporting if the reported value is above -4.5 dB"}, + { 6, "Apply priority reporting if the reported value is above -1.5 dB"}, + { 7, "Never apply priority reporting"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_network_control_order_vals[] = { + { 0, "NC0"}, + { 1, "NC1"}, + { 2, "NC2"}, + { 3, "NC0"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_nc_non_drx_period_vals[] = { + { 0, "No non-DRX mode after a measurement report has been sent"}, + { 1, "0,24 s"}, + { 2, "0,48 s"}, + { 3, "0.72 s"}, + { 4, "0.96 s"}, + { 5, "1.20 s"}, + { 6, "1.44 s"}, + { 7, "1.92 s"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_nc_reporting_period_x_vals[] = { + { 0, "0.48 s"}, + { 1, "0.96 s"}, + { 2, "1.92 s"}, + { 3, "3.84 s"}, + { 4, "7.68 s"}, + { 5, "15.36 s"}, + { 6, "30.72 s"}, + { 7, "61.44 s"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_qsearch_c_initial_value = { + "Always", + "use Qsearch I" +}; + +static const true_false_string gsm_a_rr_fdd_rep_quant_value = { + "Ec/No", + "RSCP" +}; + +static const true_false_string gsm_a_rr_3g_search_prio_value = { + "3G cells may be searched when BSIC decoding is required", + "3G cells may not be searched when BSIC decoding is required" +}; + +static const true_false_string gsm_a_rr_3g_ccn_active_value = { + "CCN towards 3G cells is enabled in the cell", + "CCN towards 3G cells is disabled in the cell" +}; + +static gint +de_rr_si2quater_meas_info_utran_fdd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, idx; + gint xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_FDD_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bandwidth FDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_fdd, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UMTS FDD Neighbour Cells", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "the FDD_CELL_INFORMATION parameter value '0000000000' ", "is a member of the set", "is not a member of the set"); + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Nr of FDD Cells : %d", idx); + curr_bit_offset += 5; + idx = convert_n_to_p[idx]; + item2 = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (idx>>3)+1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_FDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_FDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Scrambling Code: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity: %d", 0); + } + if (idx) + { + wsize = 10; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, curr_bit_offset, wsize, ENC_BIG_ENDIAN); + curr_bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + curr_bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 1024); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Scrambling Code: %d", xdd_cell_info & 0x01FF); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity: %d", (xdd_cell_info >> 9) & 0x01); + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_si2quater_meas_info_utran_tdd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + gint curr_bit_offset, idx; + gint xdd_cell_info, wsize, nwi, jwi, w[64], i, iused, xdd_indic0; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_TDD_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bandwidth TDD", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bandwidth_tdd, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UMTS TDD Neighbour Cells", "Present", "Not Present")) + { + gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Bit reserved for earlier version of protocol:", "Earlier version", "Current version"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + xdd_indic0 = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "the FDD_CELL_INFORMATION parameter value '0000000000' ", "is a member of the set", "is not a member of the set"); + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Nr of TDD Cells : %d", idx); + curr_bit_offset += 5; + idx = convert_n_to_q[idx]; + item2 = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (idx>>3)+1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_TDD_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_TDD_CELL_INFORMATION_FIELD]); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, (idx>>3)+1, "Field is %d bits long", idx); + if (xdd_indic0) + { + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Cell Parameter: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Sync Case TSTD: %d", 0); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity TDD: %d", 0); + } + if (idx) + { + wsize = 9; + nwi = 1; + jwi = 0; + i = 1; + + while (idx > 0) + { + w[i] = tvb_get_bits(tvb, curr_bit_offset, wsize, ENC_BIG_ENDIAN); + curr_bit_offset += wsize; + idx -= wsize; + if (w[i] == 0) + { + idx = 0; + break; + } + if (++jwi==nwi) + { + jwi = 0; + nwi <<= 1; + wsize--; + } + i++; + } + if (idx < 0) + { + curr_bit_offset += idx; + } + iused = i-1; + + for (i=1; i <= iused; i++) + { + xdd_cell_info = f_k(i, w, 512); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Cell Parameter: %d", xdd_cell_info & 0x07F); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Sync Case TSTD: %d", (xdd_cell_info >> 7) & 0x01); + proto_tree_add_text(subtree2,tvb, curr_bit_offset>>3, 0, "Diversity TDD: %d", (xdd_cell_info >> 8) & 0x01); + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_rtd_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + guint8 value; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD6", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start RTD", "Present", "Not Present")) + { + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start RTD: %d", idx); + curr_bit_offset += 5; + } + else + idx = 0; + + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD", "Not Present", "Present"); + while (value == 0) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "RTD index %d: %d TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits8(tvb,curr_bit_offset,6)); + curr_bit_offset += 6; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD", "Not Present", "Present"); + } + + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD struct", "Not Present", "Present"); + while(value == 0) + { /* all other frequencies */ + idx += 1; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD", "Not Present", "Present"); + while (value == 0) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "RTD index %d: %d TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits8(tvb,curr_bit_offset,6)); + curr_bit_offset += 6; + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD", "Not Present", "Present"); + } + value = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "additional RTD struct", "Not Present", "Present"); + } + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD12", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RTD12", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start RTD", "Present", "Not Present")) + { + idx = tvb_get_bits8(tvb,curr_bit_offset,5); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start RTD: %d", idx); + curr_bit_offset += 5; + } + else + idx = 0; + + while (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD", "Not Present", "Present")) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 2, "RTD index %d: %d/64 TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN)); + curr_bit_offset += 12; + } + + while(0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD Frequency", "Not Present", "Present")) + { /* all other frequencies */ + idx += 1; + while (0 == gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated RTD", "Not Present", "Present")) + { /* all the RTDs on the first frequency */ + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 2, "RTD index %d: %d/64 TDMA frame(s) modulo 51 TDMA frames", idx, tvb_get_bits16(tvb,curr_bit_offset,12,ENC_BIG_ENDIAN)); + curr_bit_offset += 12; + } + } + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_bsic_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "BA Index Start BSIC", "Present", "Not Present")) + { + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "BA Index Start BSIC: %d", tvb_get_bits8(tvb,curr_bit_offset,5)); + curr_bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + idx = tvb_get_bits8(tvb,curr_bit_offset,7); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Number Remaining BSIC: %d", idx); + curr_bit_offset += 7; + while (idx) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_frequency_scrolling, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + idx -= 1; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_report_priority_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset, idx; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + idx = tvb_get_bits8(tvb,curr_bit_offset,7); + proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, 1, "Number Cells: %d", idx); + curr_bit_offset += 7; + while (idx) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rep_priority, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + idx -= 1; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_meas_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset, rr_rest_octets_elem_idx_t id) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[id].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[id]); + if (id == DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_report_type, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_rate, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset +=1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_invalid_bsic_reporting, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset +=1; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Multiband Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_multiband_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Serving Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_serving_band_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_scale_ord, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "900 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_900_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_900_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "1800 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1800_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1800_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "400 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_400_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_400_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "1900 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1900_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_1900_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "850 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_850_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_850_reporting_threshold, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_add_meas_param_desc2(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC2]); + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "FDD Reporting Threshold2", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +/* Additions in Rel-8 */ +static const true_false_string priority_utran_start = { + "This is the first instance of the message", + "This is not the first instance of the message" +}; + +static const true_false_string priority_utran_stop = { + "This is the last instance of the message", + "This is not the last instance of the message" +}; + +static const true_false_string eutran_ccn_active = { + "CCN towards E-UTRAN cells is enabled in the cell", + "The broadcast E-UTRAN_CCN_ACTIVE parameter shall apply if applicable. Otherwise, CCN towards E-UTRAN cells is disabled in the cell" +}; + +static const true_false_string eutran_rep_quant = { + "RSRQ", + "RSRP" +}; + +static const value_string gsm_a_rr_pcid_psc_pattern_length[] = { + { 0, "1"}, + { 1, "2"}, + { 2, "3"}, + { 3, "4"}, + { 4, "5"}, + { 5, "6"}, + { 6, "7"}, + { 7, "8"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_eutran_measurement_bandwidth[] = { + { 0, "NRB = 6"}, + { 1, "NRB = 15"}, + { 2, "NRB = 25"}, + { 3, "NRB = 50"}, + { 4, "NRB = 75"}, + { 5, "NRB = 100"}, + { 6, "Reserved for future use"}, + { 7, "Reserved for future use"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_thresh_gsm_low[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "Always allowed"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_thresh_priority_search[] = { + { 0, "-98 dBm"}, + { 1, "-95 dBm"}, + { 2, "-92 dBm"}, + { 3, "-89 dBm"}, + { 4, "-86 dBm"}, + { 5, "-83 dBm"}, + { 6, "-80 dBm"}, + { 7, "-77 dBm"}, + { 8, "-74 dBm"}, + { 9, "-71 dBm"}, + {10, "-68 dBm"}, + {11, "-65 dBm"}, + {12, "-62 dBm"}, + {13, "-59 dBm"}, + {14, "-56 dBm"}, + {15, "Always search"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_utran_qrxlevmin[] = { + { 0, "-119 dBm"}, + { 1, "-117 dBm"}, + { 2, "-115 dBm"}, + { 3, "-113 dBm"}, + { 4, "-111 dBm"}, + { 5, "-109 dBm"}, + { 6, "-107 dBm"}, + { 7, "-105 dBm"}, + { 8, "-103 dBm"}, + { 9, "-101 dBm"}, + {10, "-99 dBm"}, + {11, "-97 dBm"}, + {12, "-95 dBm"}, + {13, "-93 dBm"}, + {14, "-91 dBm"}, + {15, "-89 dBm"}, + {16, "-87 dBm"}, + {17, "-85 dBm"}, + {18, "-83 dBm"}, + {19, "-81 dBm"}, + {20, "-79 dBm"}, + {21, "-77 dBm"}, + {22, "-75 dBm"}, + {23, "-73 dBm"}, + {24, "-71 dBm"}, + {25, "-69 dBm"}, + {26, "-67 dBm"}, + {27, "-65 dBm"}, + {28, "-63 dBm"}, + {29, "-61 dBm"}, + {30, "-59 dBm"}, + {31, "-57 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_thresh_utran_eutran_high_low[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "30 dB"}, + {16, "32 dB"}, + {17, "34 dB"}, + {18, "36 dB"}, + {19, "38 dB"}, + {20, "40 dB"}, + {21, "42 dB"}, + {22, "44 dB"}, + {23, "46 dB"}, + {24, "48 dB"}, + {25, "50 dB"}, + {26, "52 dB"}, + {27, "54 dB"}, + {28, "56 dB"}, + {29, "58 dB"}, + {30, "60 dB"}, + {31, "62 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_eutran_qrxlevmin[] = { + { 0, "-140 dBm"}, + { 1, "-138 dBm"}, + { 2, "-136 dBm"}, + { 3, "-134 dBm"}, + { 4, "-132 dBm"}, + { 5, "-130 dBm"}, + { 6, "-128 dBm"}, + { 7, "-126 dBm"}, + { 8, "-124 dBm"}, + { 9, "-122 dBm"}, + {10, "-120 dBm"}, + {11, "-118 dBm"}, + {12, "-116 dBm"}, + {13, "-114 dBm"}, + {14, "-112 dBm"}, + {15, "-110 dBm"}, + {16, "-108 dBm"}, + {17, "-106 dBm"}, + {18, "-104 dBm"}, + {19, "-102 dBm"}, + {20, "-100 dBm"}, + {21, "-98 dBm"}, + {22, "-96 dBm"}, + {23, "-94 dBm"}, + {24, "-92 dBm"}, + {25, "-90 dBm"}, + {26, "-88 dBm"}, + {27, "-86 dBm"}, + {28, "-84 dBm"}, + {29, "-82 dBm"}, + {30, "-80 dBm"}, + {31, "-78 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_priority_param_h_prio[] = { + { 0, "disabled"}, + { 1, "5 dB"}, + { 2, "4 dB"}, + { 3, "3 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_serving_cell_priority_param_t_reselection[] = { + { 0, "5 s"}, + { 1, "10 s"}, + { 2, "15 s"}, + { 3, "20 s"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_qsearch_c_eutran_initial[] = { + { 0, "search if signal is below -98 dBm"}, + { 1, "search if signal is below -94 dBm"}, + { 2, "search if signal is below -90 dBm"}, + { 3, "search if signal is below -86 dBm"}, + { 4, "search if signal is below -82 dBm"}, + { 5, "search if signal is below -78 dBm"}, + { 6, "search if signal is below -74 dBm"}, + { 7, "always search"}, + { 8, "search is signal is above -78 dBm"}, + { 9, "search is signal is above -74 dBm"}, + {10, "search is signal is above -70 dBm"}, + {11, "search is signal is above -66 dBm"}, + {12, "search is signal is above -62 dBm"}, + {13, "search is signal is above -58 dBm"}, + {14, "search is signal is above -54 dBm"}, + {15, "never search"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pcid_pattern_sense = { + "The group of identified cells are the one not belonging to the PCID_BITMAP_GROUP", + "The group of identified cells are the one identified by the PCID_BITMAP_GROUP" +}; + +static const true_false_string measurement_control_utran = { + "Frequency-specific search enabled: use Qsearch_C_E-UTRAN if received, otherwise use Qsearch_C_EUTRAN_Initial", + "Frequency-specific search enabled: Never search" +}; + +static const true_false_string measurement_control_eutran = { + "Frequency-specific search enabled: use Qsearch_C if received, otherwise use Qsearch_C_Initial", + "Frequency-specific search enabled: Never search" +}; + +static gint +de_rr_3g_priority_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_PRIORITY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Default UTRAN Priority Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + /* Repeated UTRAN Priority Parameters */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Priority Parameters", "Present", "Not Present")) + { + proto_tree *subtree_rep_utran_prio; + proto_item *item_rep_utran_prio; + gint rep_utran_prio_bit_offset = curr_bit_offset; + + item_rep_utran_prio = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, 1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM].strptr); + subtree_rep_utran_prio = proto_item_add_subtree(item_rep_utran_prio, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_PRIO_PARAM]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_thresh_utran_high, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Threshold UTRAN Low", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_thresh_utran_low, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "UTRAN Qrxlev Min", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree_rep_utran_prio, hf_gsm_a_rr_utran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + proto_item_set_len(item_rep_utran_prio,((curr_bit_offset-rep_utran_prio_bit_offset)>>3)+1); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_neighbour_cells(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Neighbour Cells Struct", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Bandwidth", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_bandwidth, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_thresh_eutran_high, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Threshold E-UTRAN Low", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_thresh_eutran_low, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Qrxlev Min", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_qrxlevmin, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_neighbour_cells_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NEIGHBOUR_CELLS]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated EARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Bandwidth ", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_bandwidth, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_pcid(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + gint curr_bit_offset = bit_offset; + proto_item *item; + + while (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "Repeated PCID", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "PCID Bitmap Group", "Present", "Not Present")) + { + gint i; + guint8 bitmap = tvb_get_bits8(tvb,curr_bit_offset,6); + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_bitmap_group, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (bitmap > 0) + { + proto_item_append_text(item, ": Cells IDs addressed by the bitmap:"); + } + for (i = 0; i < 6; i++) + { + if ((1 << i) & bitmap) + { + if ( i != 0) + { + proto_item_append_text(item, ","); + } + proto_item_append_text(item, " %d to %d",i*84, (i+1)*84 - 1); + } + } + curr_bit_offset += 6; + } + while (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "PCID Pattern", "Present", "Not Present")) + { + gint pcid_pattern_length; + gint pcid_pattern; + gint pattern_lower_bound, pattern_upper_bound; + gint i; + + pcid_pattern_length = tvb_get_bits8(tvb,curr_bit_offset,3) + 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_pattern_length, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + pcid_pattern = tvb_get_bits8(tvb,curr_bit_offset, pcid_pattern_length); + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s = PCID_Pattern: %d", + decode_bits_in_field(curr_bit_offset,pcid_pattern_length, pcid_pattern), + pcid_pattern); + + pattern_lower_bound = pcid_pattern << (9 - pcid_pattern_length); + pattern_upper_bound = pattern_lower_bound; + for (i = 0; i < (9-pcid_pattern_length); i++) + { + pattern_upper_bound |= 1 << i; + } + proto_item_append_text(item, ": Cells IDs addressed by the pattern: %d to %d", pattern_lower_bound, pattern_upper_bound); + + curr_bit_offset += pcid_pattern_length; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_pcid_pattern_sense, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_not_allowed_cells(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_NOT_ALLOWED_CELLS]); + + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_frequency_index, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_pcid_to_ta_mapping(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PCID_TO_TA_MAPPING]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeating PCID group", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_measurement_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + gint curr_bit_offset; + proto_item *item; + guint8 rep_quant; + + curr_bit_offset = bit_offset; + + /* E-UTRAN Measurement Parameters Description */ + proto_tree_add_bits_item(tree, hf_gsm_a_rr_qsearch_c_eutran_initial, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_rep_quant, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + rep_quant = gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN Reporting Quantity", "RSRQ", "RSRP"); + curr_bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_multirat_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + + if (!gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "Reporting", "3 bit", "6 bit")) + { + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD FDD 3 bit Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN FDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD TDD 3 bit Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + + curr_bit_offset += 3; + } + } + } + else + { + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-EUTRAN FDD Reporing Threshold", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_measurement_report_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + else + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + curr_bit_offset += 6; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN FDD Reporing Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_measurement_report_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + else + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + curr_bit_offset += 6; + + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Threshold2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, tree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + } + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_reporting_granularity, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%d dB step)", 2 + tvb_get_bits8(tvb,curr_bit_offset,1)); + } + else + { + proto_item_append_text(item, " (%d dB step)", 1 + tvb_get_bits8(tvb,curr_bit_offset,1)); + } + curr_bit_offset += 1; + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 rep_quant = 0; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_ccn_active, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* E-UTRAN Measurement Parameters Description */ + curr_bit_offset += de_rr_eutran_measurement_param_desc(tvb, subtree, curr_bit_offset); + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "GPRS E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* GPRS E-UTRAN Measurement Parameters Description */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_qsearch_p_eutran, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_rep_quant, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + rep_quant = gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Reporting Quantity", "RSRQ", "RSRP"); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_multirat_reporting, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporitng Threshold 2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN FDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_fdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporting", "Present", "Not Present")) + { + if (rep_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + } + curr_bit_offset += 3; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporitng Threshold 2", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + if (rep_quant == 0) + { + proto_item_append_text(item, " (%.1f dB)", (gfloat)tvb_get_bits8(tvb,curr_bit_offset,6)/2 - 19.5); + } + else + { + proto_item_append_text(item, " (%d dBm)", tvb_get_bits8(tvb,curr_bit_offset,6) - 140); + } + curr_bit_offset += 6; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN TDD Reporting Offset", "Present", "Not Present")) + { + item = proto_tree_add_bits_item(tree, hf_gsm_a_rr_eutran_tdd_reporting_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + } + } + + /* Repeated E-UTRAN Neighbour Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Neighbour Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_neighbour_cells(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Not Allowed Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Not Allowed Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_not_allowed_cells(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN PCID to TA mapping */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN PCID to TA mapping", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_pcid_to_ta_mapping(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_param_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Measurement Parameters Description", "Present", "Not Present")) + { + /* E-UTRAN Measurement Parameters Description */ + curr_bit_offset += de_rr_eutran_measurement_param_desc(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Neighbour Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Neighbour Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_neighbour_cells_mi(tvb, subtree, curr_bit_offset); + } + + /* Repeated E-UTRAN Not Allowed Cells */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Not Allowed Cells", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_not_allowed_cells(tvb, subtree, curr_bit_offset); + } + + /* Measurement Control Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Measurement Control Parameters Description", "Present", "Not Present")) + { + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Default Measurement Control E-UTRAN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_default_measurement_control_eutran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Measurement Control Parameters", "Present", "Not Present")) + { + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated E-UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_frequency_index, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_measurement_control_eutran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_priority_and_eutran_param_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PRIORITY_AND_EUTRAN_PARAM_DESC]); + + + /* Serving Cell Priority Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Serving Cell Priority Parameters Description", "Present", "Not Present")) + { + proto_tree *subtree_serv; + proto_item *item_serv; + gint serv_bit_offset = curr_bit_offset; + + item_serv = proto_tree_add_text(subtree, tvb, curr_bit_offset>>3, ((curr_bit_offset+15)>>3)-(curr_bit_offset>>3) + 1 , "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC].strptr); + subtree_serv = proto_item_add_subtree(item_serv, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SERVING_CELL_PRIORITY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_geran_priority, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_h_prio, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_tree_add_bits_item(subtree_serv, hf_gsm_a_rr_serving_cell_priority_param_t_reselection, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + proto_item_set_len(item_serv,((curr_bit_offset-serv_bit_offset)>>3)+1); + } + + /* 3G Priority Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Priority Parameters Description", "Present", "Not Present")) + { + curr_bit_offset += de_rr_3g_priority_param_desc(tvb, subtree, curr_bit_offset); + } + + /* E-UTRAN Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "E-UTRAN Parameters Description", "Present", "Not Present")) + { + curr_bit_offset += de_rr_eutran_param_desc(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_csg_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Repeated CSG_PSC_SPLIT struct", "Present", "Not Present")) + { + /* CSG_PSC_SPLIT struct */ + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PSC", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_psc, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "PSC Pattern", "Present", "Not Present")) + { + gint psc_pattern_length; + gint psc_pattern; + + psc_pattern_length = tvb_get_bits8(tvb,curr_bit_offset,3) + 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_psc_pattern_length, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + psc_pattern = tvb_get_bits8(tvb,curr_bit_offset, psc_pattern_length); + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s = PSC_Pattern: %d", + decode_bits_in_field(curr_bit_offset,psc_pattern_length, psc_pattern), + psc_pattern); + + curr_bit_offset += psc_pattern_length; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_psc_pattern_sense, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + } + + while(gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "repeated CSG FDD UARFCN", "Present", "Not Present")) + { + /* CSG_FDD_UARFCN */ + if (!gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Technology", "TDD", "FDD")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_csg_fdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_csg_tdd_uarfcn, tvb, curr_bit_offset, 14, ENC_BIG_ENDIAN); + curr_bit_offset += 14; + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_csg_desc(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated PCID group", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated EARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_csg_earfcn, tvb, curr_bit_offset, 16, ENC_BIG_ENDIAN); + curr_bit_offset += 16; + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_eutran_csg_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_CSG_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated CSG PCI Split", "Present", "Not Present")) + { + /* dissect PCID group */ + curr_bit_offset += de_rr_eutran_pcid(tvb, subtree, curr_bit_offset); + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_utran_measurement_control_param_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_UTRAN_MEASUREMENT_CONTROL_PARAM_DESC]); + + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Frequency Index", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_utran_frequency_index, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static gint +de_rr_3g_supplementary_param_desc_mi(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_SUPPLEMENTARY_PARAM_DESC]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_start, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_priority_param_desc_utran_stop, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + /* 3G Measurement Control Parameters Description */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Measurement Control Parameters Description", "Present", "Not Present")) + { + /* 3G Measurement Control Parameters Description struct */ + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "3G Measurement Control Parameters Description struct", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + } + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "Repeated UTRAN Measurement Control Parameters struct", "Present", "Not Present")) + { + curr_bit_offset += de_rr_utran_measurement_control_param_mi(tvb, subtree, curr_bit_offset); + } + } + + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return(curr_bit_offset - bit_offset); +} + +static guint16 +de_rr_si2quater_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2, *subtree3; + proto_item *item, *item2, *item3; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, idx; + guint8 value; + guint8 tvb_len = tvb_length(tvb); + len = 20; + curr_offset = offset; + bit_offset = curr_offset<<3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI2QUATER_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI2QUATER_REST_OCT]); + + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_index, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_count, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Measurement Parameters Description", "Present", "Not Present")) + { /* Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_gsm_report_type, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_serving_band_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Real Time Difference Description", "Present", "Not Present")) + { /* GPRS Real Time Difference Description */ + bit_offset += de_rr_rtd_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_RTD_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS BSIC Description", "Present", "Not Present")) + { /* GPRS BSIC Description */ + bit_offset += de_rr_bsic_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_BSIC_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Report Priority Description", "Present", "Not Present")) + { /* GPRS Report Priority Description */ + bit_offset += de_rr_report_priority_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_REPORT_PRIO_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS Measurement Parameters Description", "Present", "Not Present")) + { /* GPRS Measurement Parameters Description */ + bit_offset += de_rr_meas_param_desc(tvb, subtree, bit_offset, DE_RR_REST_OCTETS_GPRS_MEAS_PARAM_DESC); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "NC Measurement Parameters", "Present", "Not Present")) + { /* NC Measurement Parameters */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_NC_MEAS_PARAM].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_NC_MEAS_PARAM]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_network_control_order, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "NC Periods", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_non_drx_period, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_reporting_period_i, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nc_reporting_period_t, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI 2quater Extension Information", "Present", "Not Present")) + { /* SI 2quater Extension Information */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI2Q_EXT_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI2Q_EXT_INFO]); + idx = tvb_get_bits8(tvb,bit_offset,8); + proto_tree_add_text(subtree2,tvb, bit_offset>>3, 1, "Extension Length: %d", idx); + bit_offset += 8; + proto_item_set_len(item2,((bit_offset+idx+1-bit_offset_sav)>>3)+1); + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "CCN Support Description", "Present", "Not Present")) + { /* CCN Support Description */ + bit_offset_sav = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_CCN_SUPPORT_DESC].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_CCN_SUPPORT_DESC]); + value = tvb_get_bits8(tvb,bit_offset,7); + proto_tree_add_text(subtree3,tvb, bit_offset>>3, 1, "Number Cells: %d", value); + bit_offset += 7; + idx -= 7; + item2 = proto_tree_add_text(subtree3,tvb, bit_offset>>3, (value>>3)+1, "CCN Supported: "); + while (value) + { + proto_item_append_text(item2,"%d",tvb_get_bits8(tvb,bit_offset,1)); + bit_offset += 1; + value -= 1; + idx -= 1; + } + proto_item_set_len(item3,((bit_offset+-bit_offset_sav)>>3)+1); + } + bit_offset += idx; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Neighbour Cell Description", "Present", "Not Present")) + { /* 3G Neighbour Cell Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Index Start 3G", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_index_start_3g, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Absolute Index Start EMR", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_absolute_index_start_emr, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_fdd_desc(tvb, subtree2, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_tdd_desc(tvb, subtree2, bit_offset); + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_i, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_c_initial, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "FDD Information", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Information", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_qoffset, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "GPRS 3G Measurement Parameters Description", "Present", "Not Present")) + { /* GPRS 3G Measurement Parameters Description */ + guint8 reporting_quant = 0; + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_qsearch_p, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_3g_search_prio, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + reporting_quant = gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "Reporting Quantity", "Ec/No", "RSCP"); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "FDD Reporting Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree2, bit_offset++, "TDD Reporting Parameters", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_tdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-5", "Present", "Not present")) + { /* Additions in Rel-5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_ADD_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_qmin_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_fdd_rscpmin, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description 2", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description 2 */ + bit_offset += de_rr_3g_add_meas_param_desc2(tvb, subtree, bit_offset); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-6", "Present", "Not present")) + { /* Additions in Rel-6 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_ccn_active, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-7", "Present", "Not present")) + { /* Additions in Rel-7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "700 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_700_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_700_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "810 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_810_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_810_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + /* Additions in Rel-8 */ + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-8", "Present", "Not present")) + { /* Additions in Rel-8 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Supplementary Parameters Description ", "Present", "Not Present")) + { + bit_offset += de_rr_priority_and_eutran_param_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G CSG Description ", "Present", "Not Present")) + { + bit_offset += de_rr_3g_csg_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "EUTRAN CSG Description ", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_csg_desc(tvb, subtree, bit_offset); + } + } + } + } + } + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.34 SI 3 Rest Octets + */ +static const value_string gsm_a_rr_temporary_offset_vals[] = { + { 0, "0 dB"}, + { 1, "10 dB"}, + { 2, "20 dB"}, + { 3, "30 dB"}, + { 4, "40 dB"}, + { 5, "50 dB"}, + { 6, "60 dB"}, + { 7, "infinity"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cell_reselect_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "6 dB"}, + { 4, "8 dB"}, + { 5, "10 dB"}, + { 6, "12 dB"}, + { 7, "14 dB"}, + { 8, "16 dB"}, + { 9, "18 dB"}, + {10, "20 dB"}, + {11, "22 dB"}, + {12, "24 dB"}, + {13, "26 dB"}, + {14, "28 dB"}, + {15, "30 dB"}, + {16, "32 dB"}, + {17, "34 dB"}, + {18, "36 dB"}, + {19, "38 dB"}, + {20, "40 dB"}, + {21, "42 dB"}, + {22, "44 dB"}, + {23, "46 dB"}, + {24, "48 dB"}, + {25, "50 dB"}, + {26, "52 dB"}, + {27, "54 dB"}, + {28, "56 dB"}, + {29, "58 dB"}, + {30, "60 dB"}, + {31, "62 dB"}, + {32, "64 dB"}, + {33, "66 dB"}, + {34, "68 dB"}, + {35, "70 dB"}, + {36, "72 dB"}, + {37, "74 dB"}, + {38, "76 dB"}, + {39, "78 dB"}, + {40, "80 dB"}, + {41, "82 dB"}, + {42, "84 dB"}, + {43, "86 dB"}, + {44, "88 dB"}, + {45, "90 dB"}, + {46, "92 dB"}, + {47, "94 dB"}, + {48, "96 dB"}, + {49, "98 dB"}, + {50, "100 dB"}, + {51, "102 dB"}, + {52, "104 dB"}, + {53, "106 dB"}, + {54, "108 dB"}, + {55, "110 dB"}, + {56, "112 dB"}, + {57, "114 dB"}, + {58, "116 dB"}, + {59, "118 dB"}, + {60, "120 dB"}, + {61, "122 dB"}, + {62, "124 dB"}, + {63, "126 dB"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_penalty_time_vals[] = { + { 0, "20 s"}, + { 1, "40 s"}, + { 2, "60 s"}, + { 3, "80 s"}, + { 4, "100 s"}, + { 5, "120 s"}, + { 6, "140 s"}, + { 7, "160 s"}, + { 8, "180 s"}, + { 9, "200 s"}, + {10, "220 s"}, + {11, "240 s"}, + {12, "260 s"}, + {13, "280 s"}, + {14, "300 s"}, + {15, "320 s"}, + {16, "340 s"}, + {17, "360 s"}, + {18, "380 s"}, + {19, "400 s"}, + {20, "420 s"}, + {21, "440 s"}, + {22, "460 s"}, + {23, "480 s"}, + {24, "500 s"}, + {25, "520 s"}, + {26, "540 s"}, + {27, "560 s"}, + {28, "580 s"}, + {29, "600 s"}, + {30, "620 s"}, + {31, "Cell Reselect Offset is subtracted from C2 and Temporary Offset is ignored"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_opt_sel_param(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + if (gsm_rr_csn_HL_flag(tvb,tree,0,curr_bit_offset++,"Selection Parameters", "Present", "Not present")) + { /* Selection Parameters */ + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_OPTIONAL_SEL_PARAM]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cell_reselect_offset, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_temporary_offset, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_penalty_time, tvb, curr_bit_offset, 5, ENC_BIG_ENDIAN); + curr_bit_offset += 5; + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + } + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_si13_position_vals[] = { + { 0, "SYSTEM INFORMATION TYPE 13 message is sent on BCCH Norm"}, + { 1, "SYSTEM INFORMATION TYPE 13 message is sent on BCCH Ext"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_gprs_indicator(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, 1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_INDICATOR].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_INDICATOR]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_ra_colour, tvb, curr_bit_offset, 3, FALSE); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13_position, tvb, curr_bit_offset, 1, ENC_BIG_ENDIAN); + curr_bit_offset += 1; + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_power_offset_vals[] = { + { 0, "0 dB"}, + { 1, "2 dB"}, + { 2, "4 dB"}, + { 3, "8 dB"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_si2quater_position_value = { + "SYSTEM INFORMATION TYPE 2 quater message is sent on BCCH Ext", + "SYSTEM INFORMATION TYPE 2 quater message is sent on BCCH Norm" +}; + +static const true_false_string gsm_a_rr_si13alt_position_value = { + "If Iu mode is supported in the cell, SYSTEM INFORMATION TYPE 13alt message is sent on BCCH Ext", + "If Iu mode is supported in the cell, SYSTEM INFORMATION TYPE 13alt message is sent on BCCH Norm" +}; + +static guint16 +de_rr_si3_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset; + gboolean gprs_indicator; + + len = 4; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI3_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI3_REST_OCT]); + + bit_offset += de_rr_rest_oct_opt_sel_param(tvb, subtree, bit_offset); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Optional Power Offset", "Present", "Not present")) + { /* Optional Power Offset */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_power_offset, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SYSTEM INFORMATION TYPE 2ter", "Available", "Not Available"); + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Early Classmark Sending", "Is allowed", "Is forbidden"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Scheduling if and where", "Present", "Not present")) + { /* Scheduling if and where */ + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Where: %d",tvb_get_bits8(tvb,bit_offset,3)); + bit_offset += 3; + } + gprs_indicator = gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "GPRS Indicator", "Present", "Not present"); + if (gprs_indicator) + { /* GPRS indicator */ + bit_offset += de_rr_rest_oct_gprs_indicator(tvb, subtree, bit_offset); + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "3G Early Classmark Sending Restriction", + "The sending of UTRAN,CDMA2000 and GERAN IU MODE CLASSMARK CHANGE messages are controlled by the Early Classmark Sending Control parameter", + "Neither UTRAN, CDMA2000 nor GERAN IU MODE CLASSMARK CHANGE message shall be sent with the Early classmark sending"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SI2quater Indicator", "Present", "Not present")) + { /* SI2quater Indicator */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2quater_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gprs_indicator == FALSE) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13alt_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* + * [3] 10.5.2.32 SI 4 Rest Octets + */ +static const value_string gsm_a_rr_prio_thr_vals[] = { + { 0, "0 dB"}, + { 1, "6 dB"}, + { 2, "12 dB"}, + { 3, "18 dB"}, + { 4, "24 dB"}, + { 5, "30 dB"}, + { 6, "36 dB"}, + { 7, "Infinite"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_lsa_offset_vals[] = { + { 0, "0 dB"}, + { 1, "4 dB"}, + { 2, "8 dB"}, + { 3, "16 dB"}, + { 4, "24 dB"}, + { 5, "32 dB"}, + { 6, "48 dB"}, + { 7, "64 dB"}, + { 0, NULL } +}; + +static guint16 +de_rr_si4_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2, *subtree3; + proto_item *item, *item2, *item3; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, bit_offset_sav2; + guint value; + + guint8 tvb_len = tvb_length(tvb); + len = tvb_length_remaining(tvb,offset); + if (len==0) + return 0; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI4_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI4_REST_OCT]); + + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI4_REST_OCTETS_O].strptr); + + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI4_REST_OCTETS_O]); + + bit_offset += de_rr_rest_oct_opt_sel_param(tvb, subtree2, bit_offset); + + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "Optional Power Offset", "Present", "Not present")) + { /* Optional Power Offset */ + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_power_offset, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "GPRS Indicator", "Present", "Not present")) + { + bit_offset += de_rr_rest_oct_gprs_indicator(tvb, subtree2, bit_offset); + } + proto_item_set_len(item2,(bit_offset>>3)+1-curr_offset); + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "SI4 Rest Octets_S", "Present", "Not present")) + { /* SI4 Rest Octets_S */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SI4_REST_OCTETS_S].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SI4_REST_OCTETS_S]); + if (gsm_rr_csn_HL_flag(tvb,subtree2,curr_offset + len,bit_offset++,"LSA Parameters", "Present", "Not present")) + { /* LSA Parameters */ + bit_offset_sav2 = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_LSA_PARAMETERS].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_LSA_PARAMETERS]); + proto_tree_add_bits_item(subtree3, hf_gsm_a_rr_prio_thr, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree3, hf_gsm_a_rr_lsa_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb,subtree3, bit_offset++,"MCC/MNC", "Present", "Not Present")) + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 2, "MCC: %d", tvb_get_bits16(tvb,bit_offset,12,ENC_BIG_ENDIAN)); + bit_offset += 12; + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 2, "MNC: %d", tvb_get_bits16(tvb,bit_offset,12,ENC_BIG_ENDIAN)); + bit_offset += 12; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav2)>>3)+1); + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "Cell Identity", "Present", "Not present")) + { /* Cell Identity */ + value = tvb_get_bits16(tvb, bit_offset, 16, ENC_BIG_ENDIAN); + proto_tree_add_uint(subtree2, hf_gsm_a_bssmap_cell_ci, tvb, bit_offset>>3, 2, value); + bit_offset += 16; + } + if (gsm_rr_csn_HL_flag(tvb, subtree2, tvb_len, bit_offset++, "LSA ID information", "Present", "Not present")) + { /* LSA ID information */ + bit_offset_sav2 = bit_offset; + item3 = proto_tree_add_text(subtree2, tvb, bit_offset>>3, len, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_LSA_ID_INFO].strptr); + subtree3 = proto_item_add_subtree(item3, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_LSA_ID_INFO]); + do + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Type:", "Short LSA ID", "LSA ID")) + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 3, "Short LSA ID: %d",tvb_get_bits16(tvb,bit_offset,10,ENC_BIG_ENDIAN)); + bit_offset += 10; + } + else + { + proto_tree_add_text(subtree3, tvb, bit_offset>>3, 3, "LSA ID: %d",tvb_get_bits32(tvb,bit_offset,24,ENC_BIG_ENDIAN)); + bit_offset += 24; + } + } while (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Additional LSA ID", "Present", "Not Present")); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "CBQ3", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_cbq3, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI3 alt position", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13alt_position, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + else + { /* Break indicator */ + gsm_rr_csn_HL_flag(tvb, subtree, tvb_len, bit_offset++, "Break Indicator", + "Additional parameters \"SI4 Rest Octets_S\" are sent in SYSTEM INFORMATION TYPE 7 and 8", + "Additional parameters \"SI4 Rest Octets_S\" are not sent in SYSTEM INFORMATION TYPE 7 and 8"); + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + curr_offset = tvb_len; + return curr_offset-offset; +} + +/* + * [3] 10.5.2.35a SI 6 Rest Octets + */ +static const true_false_string gsm_a_rr_paging_channel_restructuring_value = { + "Paging channel is restructured", + "Paging channel is not restructured" +}; + +static const true_false_string gsm_a_rr_vbs_vgcs_inband_notifications_value = { + "The mobile shall be notified on incoming high priority VBS/VGCS calls through NOTIFICATION/FACCH, the mobile need not to inspect the NCH", + "The network does not provide notification on FACCH so that the mobile should inspect the NCH for notifications" +}; + +static const true_false_string gsm_a_rr_vbs_vgcs_inband_pagings_value = { + "The mobile shall be notified on incoming high priority point-to-point calls through NOTIFICATION/FACCH, the mobile need not to inspect the PCH", + "The network does not provide paging information on FACCH so that the mobile should inspect the PCH for pagings" +}; + +static const value_string gsm_a_rr_max_lapdm_vals[] = { + { 0, "Any message segmented in up to 5 LAPDm frames"}, + { 1, "Any message segmented in up to 6 LAPDm frames"}, + { 2, "Any message segmented in up to 7 LAPDm frames"}, + { 3, "Any message segmented in up to 8 LAPDm frames"}, + { 4, "Any message segmented in up to 9 LAPDm frames"}, + { 5, "Any message segmented in up to 10 LAPDm frames"}, + { 6, "Any message segmented in up to 11 LAPDm frames"}, + { 7, "Any message segmented in up to 12 LAPDm frames"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_dedicated_mode_mbms_notification_support_value = { + "The cell supports the Dedicated Mode MBMS Notification procedures", + "The cell does not support the Dedicated Mode MBMS Notification procedures" +}; + +static const true_false_string gsm_a_rr_mnci_support_value = { + "The cell supports the distribution of MBMS NEIGHBOURING CELL INFORMATION messages", + "The cell does not support the distribution of MBMS NEIGHBOURING CELL INFORMATION messages" +}; + +static guint16 +de_rr_si6_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + guint8 value; + + len = 7; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI6_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI6_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "PCH and NCH Info", "Present", "Not present")) + { /* PCH and NCH Info */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PCH_AND_NCH_INFO].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PCH_AND_NCH_INFO]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_paging_channel_restructuring, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nln_sacch, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Call Priority", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_call_prio, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_nln_status_sacch, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "VBS/VGCS options", "Present", "Not present")) + { /* VBS/VGCS options */ + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb,bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_VBS_VGCS_OPTIONS]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_vbs_vgcs_inband_notifications, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_vbs_vgcs_inband_pagings, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + value = gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "DTM", "Supported in Serving cell", "Not Supported in Serving cell"); + if (value == TRUE) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rac, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_max_lapdm, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Band Indicator", "1900", "1800"); + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "GPRS MS PWR MAX CCCH", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_gprs_ms_txpwr_max_ccch, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "MBMS Procedures", "Supported", "Not supported")) + { /* MBMS Procedures */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dedicated_mode_mbms_notification_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mnci_support, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-7", "Present", "Not present")) + { /* Additions in Release 7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "AMR Config", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_amr_config, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + +/* + * [3] 10.5.2.37b SI 13 Rest Octets + */ +static const value_string gsm_a_rr_si_change_field_vals[] = { + { 0, "Update of unspecified SI message or SI messages"}, + { 1, "Update of SI1 message"}, + { 2, "Update of SI2, SI2 bis or SI2 ter message or any instance of SI2quater messages"}, + { 3, "Update of SI3, SI4, SI7, SI8, SI16 or SI17 message"}, + { 4, "Update of SI9 message"}, + { 5, "Update of SI18 or SI20 message"}, + { 6, "Update of SI19 message"}, + { 7, "Update of SI15 message"}, + { 8, "Update of SI2n message"}, + { 9, "Update of unknown SI message type"}, + { 10, "Update of unknown SI message type"}, + { 11, "Update of unknown SI message type"}, + { 12, "Update of unknown SI message type"}, + { 13, "Update of unknown SI message type"}, + { 14, "Update of unknown SI message type"}, + { 15, "Update of unknown SI message type"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_psi1_repeat_period_vals[] = { + { 0, "1 multiframe"}, + { 1, "2 multiframes"}, + { 2, "3 multiframes"}, + { 3, "4 multiframes"}, + { 4, "5 multiframes"}, + { 5, "6 multiframes"}, + { 6, "7 multiframes"}, + { 7, "8 multiframes"}, + { 8, "9 multiframes"}, + { 9, "10 multiframes"}, + { 10, "11 multiframes"}, + { 11, "12 multiframes"}, + { 12, "13 multiframes"}, + { 13, "14 multiframes"}, + { 14, "15 multiframes"}, + { 15, "16 multiframes"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_pbcch_pb_vals[] = { + { 0, "0 dB"}, + { 1, "-2 dB"}, + { 2, "-4 dB"}, + { 3, "-6 dB"}, + { 4, "-8 dB"}, + { 5, "-10 dB"}, + { 6, "-12 dB"}, + { 7, "-14 dB"}, + { 8, "-16 dB"}, + { 9, "-18 dB"}, + { 10, "-20 dB"}, + { 11, "-22 dB"}, + { 12, "-24 dB"}, + { 13, "-26 dB"}, + { 14, "-28 dB"}, + { 15, "-30 dB"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_spgc_ccch_sup_value = { + "SPLIT_PG_CYCLE is supported on CCCH in this cell", + "SPLIT_PG_CYCLE is not supported on CCCH in this cell" +}; + +static const value_string gsm_a_rr_priority_access_thr_vals[] = { + { 0, "Packet access is not allowed in the cell"}, + { 1, "Packet access is not allowed in the cell"}, + { 2, "Packet access is not allowed in the cell"}, + { 3, "Packet access is allowed for priority level 1"}, + { 4, "Packet access is allowed for priority level 1 to 2"}, + { 5, "Packet access is allowed for priority level 1 to 3"}, + { 6, "Packet access is allowed for priority level 1 to 4"}, + { 7, "Packet access is allowed for priority level 1 to 4"}, + { 0, NULL } +}; + +static gint +de_rr_rest_oct_gprs_mobile_allocation(tvbuff_t *tvb, proto_tree *tree, gint bit_offset) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + guint8 value; + guint64 ma_length; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_GPRS_MOBILE_ALLOC]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_hsn, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "RFL number list", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rfl_number, tvb, curr_bit_offset, 4, ENC_BIG_ENDIAN); + curr_bit_offset += 4; + } + if (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "MA", "Not Present", "Present")) + { + while (gsm_rr_csn_flag(tvb, subtree, curr_bit_offset++, "ARFCN index list", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_arfcn_index, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + } + } + else + { + proto_tree_add_bits_ret_val(subtree, hf_gsm_a_rr_ma_length, tvb, curr_bit_offset, 6, &ma_length, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + value = (gint)ma_length + 1; + item = proto_tree_add_text(subtree,tvb, curr_bit_offset>>3, (value>>3)+1, "MA Bitmap: "); + while (value) + { + proto_item_append_text(item,"%d",tvb_get_bits8(tvb,curr_bit_offset,1)); + curr_bit_offset += 1; + value -= 1; + } + } + proto_item_set_len(item,((curr_bit_offset-bit_offset)>>3)+1); + + return (curr_bit_offset - bit_offset); +} + +static const value_string gsm_a_rr_nmo_vals[] = { + { 0, "Network Mode of Operation I"}, + { 1, "Network Mode of Operation II"}, + { 2, "Network Mode of Operation III"}, + { 3, "Reserved"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t3168_vals[] = { + { 0, "500 ms"}, + { 1, "1000 ms"}, + { 2, "1500 ms"}, + { 3, "2000 ms"}, + { 4, "2500 ms"}, + { 5, "3000 ms"}, + { 6, "3500 ms"}, + { 7, "4000 ms"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t3192_vals[] = { + { 0, "500 ms"}, + { 1, "1000 ms"}, + { 2, "1500 ms"}, + { 3, "0 ms"}, + { 4, "80 ms"}, + { 5, "120 ms"}, + { 6, "160 ms"}, + { 7, "200 ms"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_drx_timer_max_vals[] = { + { 0, "0 s"}, + { 1, "1 s"}, + { 2, "2 s"}, + { 3, "4 s"}, + { 4, "8 s"}, + { 5, "16 s"}, + { 6, "32 s"}, + { 7, "64 s"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_access_burst_type_value = { + "11-bit format shall be used", + "8-bit format shall be used" +}; + +static const true_false_string gsm_a_rr_control_ack_type_value = { + "Default format is RLC/MAC control block", + "Default format is four access bursts" +}; + +static const value_string gsm_a_rr_pan_max_vals[] = { + { 0, "maximum value allowed for counter N3102 is 4"}, + { 1, "maximum value allowed for counter N3102 is 8"}, + { 2, "maximum value allowed for counter N3102 is 12"}, + { 3, "maximum value allowed for counter N3102 is 16"}, + { 4, "maximum value allowed for counter N3102 is 20"}, + { 5, "maximum value allowed for counter N3102 is 24"}, + { 6, "maximum value allowed for counter N3102 is 28"}, + { 7, "maximum value allowed for counter N3102 is 32"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_egprs_packet_channel_request_value = { + "Use two phase packet access with PACKET CHANNEL REQUEST message for uplink TBF establishment on the PRACH", + "Use EGPRS PACKET CHANNEL REQUEST message for uplink TBF establishment on the PRACH" +}; + +static const value_string gsm_a_rr_bep_period_vals[] = { + { 0, "1"}, + { 1, "2"}, + { 2, "3"}, + { 3, "4"}, + { 4, "5"}, + { 5, "7"}, + { 6, "10"}, + { 7, "12"}, + { 8, "15"}, + { 9, "20"}, + { 10, "25"}, + { 11, "Reserved"}, + { 12, "Reserved"}, + { 13, "Reserved"}, + { 14, "Reserved"}, + { 15, "Reserved"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pfc_feature_mode_value = { + "The network supports packet flow context procedures", + "The network does not support packet flow context procedures" +}; + +static const true_false_string gsm_a_rr_dtm_support_value = { + "The cell supports DTM procedures", + "The cell does not support DTM procedures" +}; + +static const true_false_string gsm_a_rr_bss_paging_coordination_value = { + "The cell supports Circuit-Switched paging coordination", + "The cell does not support Circuit-Switched paging coordination" +}; + +static const true_false_string gsm_a_rr_ccn_active_value = { + "CCN is enabled in the cell", + "CCN is disabled in the cell" +}; + +static const true_false_string gsm_a_rr_nw_ext_utbf_value = { + "The extended uplink TBF mode is supported by the network", + "The extended uplink TBF mode is not supported by the network" +}; + +static const true_false_string gsm_a_rr_multiple_tbf_capability_value = { + "The cell supports multiple TBF procedures", + "The cell does not support multiple TBF procedures" +}; + +static const true_false_string gsm_a_rr_ext_utbf_no_data_value = { + "The mobile station may refrain from sending a PACKET UPLINK DUMMY CONTROL BLOCK message when there is no other RLC/MAC block ready to send in an uplink radio block allocated by the network", + "The mobile station shall send a PACKET UPLINK DUMMY CONTROL BLOCK message when there is no other RLC/MAC block ready to send in an uplink radio block allocated by the network" +}; + +static const true_false_string gsm_a_rr_dtm_enhancements_capability_value = { + "The cell supports enhanced DTM CS establishment and enhanced DTM CS release procedures", + "The cell does not support enhanced DTM CS establishment and enhanced DTM CS release procedures" +}; + +static const true_false_string gsm_a_rr_reduced_latency_access_value = { + "The cell supports \"One Phase Access Request by Reduced Latency MS\"", + "The cell does not support \"One Phase Access Request by Reduced Latency MS\"" +}; + +static const value_string gsm_a_rr_alpha_vals[] = { + { 0, "0.0"}, + { 1, "0.1"}, + { 2, "0.2"}, + { 3, "0.3"}, + { 4, "0.4"}, + { 5, "0.5"}, + { 6, "0.6"}, + { 7, "0.7"}, + { 8, "0.8"}, + { 9, "0.9"}, + { 10, "1.0"}, + { 11, "1.0"}, + { 12, "1.0"}, + { 13, "1.0"}, + { 14, "1.0"}, + { 15, "1.0"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_t_avg_x_vals[] = { + { 0, "2^(0/2) / 6 multiframes"}, + { 1, "2^(1/2) / 6 multiframes"}, + { 2, "2^(2/2) / 6 multiframes"}, + { 3, "2^(3/2) / 6 multiframes"}, + { 4, "2^(4/2) / 6 multiframes"}, + { 5, "2^(5/2) / 6 multiframes"}, + { 6, "2^(6/2) / 6 multiframes"}, + { 7, "2^(7/2) / 6 multiframes"}, + { 8, "2^(8/2) / 6 multiframes"}, + { 9, "2^(9/2) / 6 multiframes"}, + { 10, "2^(10/2) / 6 multiframes"}, + { 11, "2^(11/2) / 6 multiframes"}, + { 12, "2^(12/2) / 6 multiframes"}, + { 13, "2^(13/2) / 6 multiframes"}, + { 14, "2^(14/2) / 6 multiframes"}, + { 15, "2^(15/2) / 6 multiframes"}, + { 16, "2^(16/2) / 6 multiframes"}, + { 17, "2^(17/2) / 6 multiframes"}, + { 18, "2^(18/2) / 6 multiframes"}, + { 19, "2^(19/2) / 6 multiframes"}, + { 20, "2^(20/2) / 6 multiframes"}, + { 21, "2^(21/2) / 6 multiframes"}, + { 22, "2^(22/2) / 6 multiframes"}, + { 23, "2^(23/2) / 6 multiframes"}, + { 24, "2^(24/2) / 6 multiframes"}, + { 25, "2^(25/2) / 6 multiframes"}, + { 26, "2^(25/2) / 6 multiframes"}, + { 27, "2^(25/2) / 6 multiframes"}, + { 28, "2^(25/2) / 6 multiframes"}, + { 29, "2^(25/2) / 6 multiframes"}, + { 30, "2^(25/2) / 6 multiframes"}, + { 31, "2^(25/2) / 6 multiframes"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_pc_meas_chan_value = { + "Downlink measurements for power control shall be made on PDCH", + "Downlink measurements for power control shall be made on BCCH" +}; + +static const value_string gsm_a_rr_n_avg_i_vals[] = { + { 0, "2^(0/2)"}, + { 1, "2^(1/2)"}, + { 2, "2^(2/2)"}, + { 3, "2^(3/2)"}, + { 4, "2^(4/2)"}, + { 5, "2^(5/2)"}, + { 6, "2^(6/2)"}, + { 7, "2^(7/2)"}, + { 8, "2^(8/2)"}, + { 9, "2^(9/2)"}, + { 10, "2^(10/2)"}, + { 11, "2^(11/2)"}, + { 12, "2^(12/2)"}, + { 13, "2^(13/2)"}, + { 14, "2^(14/2)"}, + { 15, "2^(15/2)"}, + { 0, NULL } +}; + +static const true_false_string gsm_a_rr_sgsnr_value = { + "SGSN is Release '99 onwards", + "SGSN is Release '98 or older" +}; + +static const true_false_string gsm_a_rr_si_status_ind_value = { + "The network supports the PACKET SI STATUS message", + "The network does not support the PACKET SI STATUS message" +}; + +static const value_string gsm_a_rr_lb_ms_txpwr_max_cch_vals[] = { + { 0, "43 dBm"}, + { 1, "41 dBm"}, + { 2, "39 dBm"}, + { 3, "37 dBm"}, + { 4, "35 dBm"}, + { 5, "33 dBm"}, + { 6, "31 dBm"}, + { 7, "29 dBm"}, + { 8, "27 dBm"}, + { 9, "25 dBm"}, + { 10, "23 dBm"}, + { 11, "21 dBm"}, + { 12, "19 dBm"}, + { 13, "17 dBm"}, + { 14, "15 dBm"}, + { 15, "13 dBm"}, + { 16, "11 dBm"}, + { 17, "9 dBm"}, + { 18, "7 dBm"}, + { 19, "5 dBm"}, + { 20, "5 dBm"}, + { 21, "5 dBm"}, + { 22, "5 dBm"}, + { 23, "5 dBm"}, + { 24, "5 dBm"}, + { 25, "5 dBm"}, + { 26, "5 dBm"}, + { 27, "5 dBm"}, + { 28, "5 dBm"}, + { 29, "5 dBm"}, + { 30, "5 dBm"}, + { 31, "5 dBm"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_si2n_support_vals[] = { + { 0, "SI2n is not supported"}, + { 1, "SI2n is supported on PACCH"}, + { 2, "SI2n is supported on PACCH and broadcast on BCCH"}, + { 3, "SI2n is supported on PACCH and broadcast on BCCH Ext"}, + { 0, NULL } +}; + +static guint16 +de_rr_si13_rest_oct(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree, *subtree2; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + + len = 20; + curr_offset = offset; + bit_offset = curr_offset << 3; + + item = proto_tree_add_text(tree, tvb, curr_offset, len, "%s", + gsm_rr_elem_strings[DE_RR_SI13_REST_OCT].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_SI13_REST_OCT]); + + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "SI13 contents", "Present", "Not present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_change_mark, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si_change_field, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "SI13 Change Mark", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si13_change_mark, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + bit_offset += de_rr_rest_oct_gprs_mobile_allocation(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "PBCCH", "Present In Cell", "Not Present In Cell")) + { /* PBCCH present in the cell */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_psi1_repeat_period, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + bit_offset_sav = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_PBCCH_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_PBCCH_DESC]); + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_pb, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_tsc, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_pbcch_tn, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "MAIO", "Present", "Not Present")) + { + proto_tree_add_text(subtree2, tvb, bit_offset>>3, 1, "MAIO: %d", tvb_get_bits8(tvb,bit_offset,6)); + bit_offset += 6; + } + else + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "ARFCN", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree2, hf_gsm_a_rr_arfcn, tvb, bit_offset, 10, ENC_BIG_ENDIAN); + bit_offset += 10; + } + else + proto_tree_add_text(subtree2, tvb, bit_offset>>3, 1, "PBCCH shall use the BCCH carrier"); + } + proto_item_set_len(item2,((bit_offset-bit_offset_sav)>>3)+1); + } + else + { /* PBCCH not present in the cell */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rac, tvb, bit_offset, 8, ENC_BIG_ENDIAN); + bit_offset += 8; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_spgc_ccch_sup, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_priority_access_thr, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_network_control_order, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + bit_offset += de_rr_rest_oct_gprs_cell_options(tvb, subtree, bit_offset); + bit_offset += de_rr_rest_oct_gprs_power_control_parameters(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in R99", "Present", "Not present")) + { /* Additions in release 99 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_sgsnr, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-4", "Present", "Not present")) + { /* Additions in release Rel-4 */ + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si_status_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_HL_flag(tvb, subtree, 0, bit_offset++, "Additions in Rel-6", "Present", "Not present")) + { /* Additions in release Rel-6 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "LB MS TXPWR MAX CCH ", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_lb_ms_txpwr_max_cch, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + } + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_si2n_support, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); + return tvb_length(tvb) - offset; +} + +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets + */ +/* + * [3] 10.5.2.38 Starting Time + */ +static guint16 +de_rr_starting_time(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_item *item; + guint32 curr_offset; + guint16 rfn, fn; + + curr_offset = offset; + + fn = tvb_get_ntohs(tvb,curr_offset); + rfn = reduced_frame_number(fn); + proto_tree_add_item(tree, hf_gsm_a_rr_T1prim, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_T3, tvb, curr_offset, 2, ENC_BIG_ENDIAN); + curr_offset++; + proto_tree_add_item(tree, hf_gsm_a_rr_T2, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + item = proto_tree_add_uint(tree, hf_gsm_a_rr_rfn, tvb, curr_offset-2, 2, rfn); + PROTO_ITEM_SET_GENERATED(item); + return(curr_offset - offset); +} +/* + * [3] 10.5.2.39 Synchronization Indication + */ +/* + * ROT: Report Observed Time Difference (Octet1 bit 3) */ + +static const true_false_string sm_a_rr_sync_ind_rot_value = { + "Mobile Time Difference IE shall be included in the HANDOVER COMPLETE message", + "Mobile Time Difference IE shall not be included in the HANDOVER COMPLETE message" +}; + +/* SI: Synchronization indication (octet 1)Bit2 1 */ + +static const value_string gsm_a_rr_sync_ind_si_vals[] = { + { 0, "Non-synchronized"}, + { 1, "Synchronized"}, + { 2, "Pre-synchronised"}, + { 3, "Pseudo-synchronised"}, + { 0, NULL } +}; +/* NCI: Normal cell indication (octet 1, bit 4) */ + +static const true_false_string gsm_a_rr_sync_ind_nci_value = { + "Out of range timing advance shall trigger a handover failure procedure", + "Out of range timing advance is ignored" +}; +static guint16 +de_rr_sync_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + /*NCI */ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_nci, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*ROT */ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_rot, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + /*SI*/ + proto_tree_add_item(tree, hf_gsm_a_rr_sync_ind_si, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.40 Timing Advance + */ +static guint16 +de_rr_timing_adv(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_timing_adv, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.41 Time Difference + */ +static guint16 +de_rr_time_diff(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_time_diff, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.41a TLLI + * The TLLI is encoded as a binary number with a length of 4 octets. TLLI is defined in 3GPP TS 23.003 + */ +guint16 +de_rr_tlli(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset, tlli; + + curr_offset = offset; + + tlli = tvb_get_ntohl(tvb, curr_offset); + proto_tree_add_item(tree, hf_gsm_a_rr_tlli, tvb, curr_offset, 4, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 4; + if(add_string) + g_snprintf(add_string, string_len, " - 0x%x", tlli); + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42 TMSI/P-TMSI + */ +static guint16 +de_rr_tmsi_ptmsi(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_TMSI_PTMSI].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_TMSI_PTMSI]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_tmsi_ptmsi, tvb, curr_offset, 4, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 4; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42a VGCS target mode Indication + */ +/* +Target mode (octet 3) +Bit 8 7 + 0 0 dedicated mode + 0 1 group transmit mode + Other values are reserved for future use. +*/ +static const value_string gsm_a_rr_target_mode_vals[] _U_ = { + { 0, "Dedicated mode"}, + { 1, "Group transmit mode"}, + { 0, NULL } +}; +static guint16 +de_rr_vgcs_tar_mode_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_target_mode, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tree, hf_gsm_a_rr_group_cipher_key_number, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.42b VGCS Ciphering Parameters + */ +static guint16 +de_rr_vgcs_cip_par(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_text(tree,tvb, curr_offset, len ,"Data(Not decoded)"); + + curr_offset = curr_offset + 2; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.43 Wait Indication + */ +static guint16 +de_rr_wait_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_wait_indication, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.44 SI10 rest octets $(ASCI)$ + */ + +/* + * [3] 10.5.2.45 Extended Measurement Results + */ +static guint16 +de_rr_ext_meas_result(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + gint bit_offset, i; + guint8 value; + + curr_offset = offset; + len = 16; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_seq_code, tvb,bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_dtx_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + for (i=0; i<21; i++) + { + value = tvb_get_bits8(tvb,bit_offset,6); + proto_tree_add_text(tree, tvb, bit_offset>>3, 1, "RXLEV carrier %d: %s (%d)",i,val_to_str_ext(value, &gsm_a_rr_rxlev_vals_ext, "Unknown"),value); + bit_offset += 6; + } + + curr_offset = offset + len; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.46 Extended Measurement Frequency List + */ +static guint16 +de_rr_ext_meas_freq_list(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_seq_code, tvb,(curr_offset<<3)+3, 1, ENC_BIG_ENDIAN); + + return dissect_arfcn_list(tvb, tree, pinfo, offset, 16, add_string, string_len); +} + +/* + * [3] 10.5.2.47 Suspension Cause + */ +/*Suspension cause value (octet 2)*/ +static const value_string gsm_a_rr_suspension_cause_vals[] = { + { 0, "Emergency call, mobile originating call or call re-establishment"}, + { 1, "Location Area Update"}, + { 2, "MO Short message service"}, + { 3, "Other procedure which can be completed with an SDCCH"}, + { 4, "MO Voice broadcast or group call"}, + { 5, "Mobile terminating CS connection"}, + { 6, "DTM not supported in the cell"}, + { 0, NULL } +}; +guint16 +de_rr_sus_cau(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_suspension_cause, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 1; + + return(curr_offset - offset); +} +/* + * [3] 10.5.2.48 APDU ID + */ +static const value_string gsm_a_rr_apdu_id_vals[] = { + { 0, "RRLP (GSM 04.31) LCS" }, + { 0, NULL }, +}; +static guint16 +de_rr_apdu_id(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree_add_item(tree, hf_gsm_a_rr_apdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); + + return 0; +} + +/* + * [3] 10.5.2.49 APDU Flags + */ +static const value_string gsm_a_rr_apdu_flags_vals[] = { + { 1, "Last or only segment" }, + { 2, "First or only segment" }, + { 0, NULL }, +}; +static guint16 +de_rr_apdu_flags(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree_add_item(tree, hf_gsm_a_rr_apdu_flags, tvb, offset, 1, ENC_BIG_ENDIAN); + + return 1; +} + +/* + * [3] 10.5.2.50 APDU Data + */ +static guint16 +de_rr_apdu_data(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + tvbuff_t *sub_tvb; + + sub_tvb = tvb_new_subset(tvb, offset, len, len); + + if (rrlp_dissector) + call_dissector(rrlp_dissector, sub_tvb,pinfo, tree); + + return len; +} + +/* + * [3] 10.5.2.51 Handover To UTRAN Command + */ +static guint16 +de_rr_ho_to_utran_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + tvbuff_t *rrc_irat_ho_to_utran_cmd_tvb; + + curr_offset = offset; + if (len) + { + rrc_irat_ho_to_utran_cmd_tvb = tvb_new_subset(tvb, curr_offset, len, len); + if (rrc_irat_ho_to_utran_cmd_handle) + call_dissector(rrc_irat_ho_to_utran_cmd_handle, rrc_irat_ho_to_utran_cmd_tvb, pinfo, tree); + } + + curr_offset += len; + return(curr_offset - offset); +} + + +/* + * [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell + */ +/* + * 10.5.2.57 Service Support + */ +static const true_false_string gsm_a_rr_MBMS_multicast_value = { + "mobile station requires notification of multicast MBMS services", + "mobile station does not require notification of multicast MBMS services" +}; +static const true_false_string gsm_a_rr_MBMS_broadcast_value = { + "mobile station requires notification of broadcast MBMS services", + "mobile station does not require notification of broadcast MBMS services" +}; +static guint16 +de_rr_serv_sup(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + /* bit 1 + * 0 mobile station does not require notification of broadcast MBMS services + * 1 mobile station requires notification of broadcast MBMS services + * bit 2 + * 0 mobile station does not require notification of multicast MBMS services + * 1 mobile station requires notification of multicast MBMS services + */ + /* MBMS Multicast */ + proto_tree_add_item(tree, hf_gsm_a_rr_MBMS_multicast, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + /* MBMS Broadcast */ + proto_tree_add_item(tree, hf_gsm_a_rr_MBMS_broadcast, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + curr_offset++; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.59 Dedicated Service Information + */ +/* +Last Segment (octet 2) +bit 1 + 0 mobile station shall not perform Service Information Sending procedure on new cell. + 1 mobile station shall perform Service Information Sending procedure on new cell. +*/ +static const true_false_string gsm_a_rr_last_segment_value = { + "Mobile station shall perform Service Information Sending procedure on new cell.", + "mobile station shall not perform Service Information Sending procedure on new cell." +}; +static guint16 +de_rr_ded_serv_inf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + guint32 curr_offset; + + curr_offset = offset; + + proto_tree_add_item(tree, hf_gsm_a_rr_last_segment, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset = curr_offset + 3; + + return(curr_offset - offset); +} + +/* + * [3] 10.5.2.69 Carrier Indication + */ +static const true_false_string gsm_a_rr_carrier_ind_value = { + "Carrier 2", + "Carrier 1" +}; + +static guint16 +de_rr_carrier_ind(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + + curr_offset = offset; + + item = proto_tree_add_text(tree, tvb, curr_offset, 3, "%s", + gsm_rr_elem_strings[DE_RR_CARRIER_IND].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_rr_elem[DE_RR_CARRIER_IND]); + + proto_tree_add_item(subtree, hf_gsm_a_rr_carrier_ind, tvb, curr_offset, 1, ENC_BIG_ENDIAN); + + curr_offset += 1; + + return(curr_offset - offset); +} + +guint16 (*rr_elem_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len, gchar *add_string, int string_len) = { + /* Radio Resource Management Information Elements 10.5.2, most are from 10.5.1 */ + + de_rr_ba_range, /* [3] 10.5.2.1a BA Range */ + de_rr_cell_ch_dsc, /* [3] 10.5.2.1b Cell Channel Description */ + de_rr_ba_list_pref, /* [3] 10.5.2.1c BA List Pref */ + de_rr_utran_freq_list, /* [3] 10.5.2.1d UTRAN Frequency List */ + de_rr_cell_select_indic, /* [3] 10.5.2.1e Cell selection indicator after release of all TCH and SDCCH IE */ + de_rr_cell_dsc, /* 10.5.2.2 RR Cell Description */ + de_rr_cell_opt_bcch, /* [3] 10.5.2.3 Cell Options (BCCH) */ + de_rr_cell_opt_sacch, /* [3] 10.5.2.3a Cell Options (SACCH) */ + de_rr_cell_sel_param, /* [3] 10.5.2.4 Cell Selection Parameters */ +/* + * [3] 10.5.2.4a (void) + */ + de_rr_ch_dsc, /* [3] 10.5.2.5 Channel Description */ + de_rr_ch_dsc2, /* [3] 10.5.2.5a RR Channel Description 2 */ + de_rr_ch_dsc3, /* [3] 10.5.2.5c RR Channel Description 3 */ + de_rr_ch_mode, /* [3] 10.5.2.6 Channel Mode */ + de_rr_ch_mode2, /* [3] 10.5.2.7 Channel Mode 2 */ + de_rr_utran_cm, /* [3] 10.5.2.7a UTRAN Classmark */ +/* [3] 10.5.2.7b (void) */ + + de_rr_cm_enq_mask, /* [3] 10.5.2.7c Classmark Enquiry Mask */ +/* [3] 10.5.2.7d GERAN Iu Mode Classmark information element */ + de_rr_chnl_needed, /* [3] 10.5.2.8 Channel Needed + * [3] 10.5.2.8a (void) + * [3] 10.5.2.8b Channel Request Description 2 */ + /* Pos 20 */ + de_rr_cip_mode_set, /* [3] 10.5.2.9 Cipher Mode Setting */ + de_rr_cip_mode_resp, /* [3] 10.5.2.10 Cipher Response */ + de_rr_ctrl_ch_desc, /* [3] 10.5.2.11 Control Channel Description */ +/* [3] 10.5.2.11a DTM Information Details */ + de_rr_dyn_arfcn_map, /* [3] 10.5.2.11b Dynamic ARFCN Mapping */ + de_rr_freq_ch_seq, /* [3] 10.5.2.12 Frequency Channel Sequence */ + de_rr_freq_list, /* [3] 10.5.2.13 Frequency List */ + de_rr_freq_short_list, /* [3] 10.5.2.14 Frequency Short List */ + de_rr_freq_short_list2, /* [3] 10.5.2.14a Frequency Short List 2 */ +/* [3] 10.5.2.14b Group Channel Description */ + de_rr_gprs_resumption, /* [3] 10.5.2.14c GPRS Resumption */ + de_rr_gprs_broadcast_info, /* [3] 10.5.2.14d GPRS broadcast information */ +/* [3] 10.5.2.14e Enhanced DTM CS Release Indication */ + de_rr_ho_ref, /* 10.5.2.15 Handover Reference */ + de_rr_ia_rest_oct, /* [3] 10.5.2.16 IA Rest Octets */ + de_rr_iar_rest_oct, /* [3] 10.5.2.17 IAR Rest Octets */ + de_rr_iax_rest_oct, /* [3] 10.5.2.18 IAX Rest Octets */ + de_rr_l2_pseudo_len, /*[3] 10.5.2.19 L2 Pseudo Length */ + de_rr_meas_res, /* [3] 10.5.2.20 Measurement Results */ +/* + * [3] (void) + */ + de_rr_mob_all, /* [3] 10.5.2.21 Mobile Allocation */ + de_rr_mob_time_diff, /* [3] 10.5.2.21a Mobile Time Difference */ + de_rr_multirate_conf, /* [3] 10.5.2.21aa MultiRate configuration */ + /* Pos 30 */ + de_rr_mult_all, /* [3] 10.5.2.21b Multislot Allocation */ +/* + * [3] 10.5.2.21c (void) + */ + de_rr_neigh_cell_desc, /* [3] 10.5.2.22 Neighbour Cell Description */ + de_rr_neigh_cell_desc2, /* [3] 10.5.2.22a Neighbour Cell Description 2 */ +/* + * [3] 10.5.2.22b (void) + * [3] 10.5.2.22c NT/N Rest Octets */ + de_rr_p1_rest_oct, /* [3] 10.5.2.23 P1 Rest Octets */ + de_rr_p2_rest_oct, /* [3] 10.5.2.24 P2 Rest Octets */ + de_rr_p3_rest_oct, /* [3] 10.5.2.25 P3 Rest Octets */ + de_rr_packet_ch_desc, /* [3] 10.5.2.25a Packet Channel Description */ + de_rr_ded_mod_or_tbf, /* [3] 10.5.2.25b Dedicated mode or TBF */ +/* [3] 10.5.2.25c RR Packet Uplink Assignment + * [3] 10.5.2.25d RR Packet Downlink Assignment + */ + de_rr_page_mode, /* [3] 10.5.2.26 Page Mode */ +/* + * [3] 10.5.2.26a (void) + * [3] 10.5.2.26b (void) + * [3] 10.5.2.26c (void) + * [3] 10.5.2.26d (void) + */ + de_rr_ncc_perm, /* [3] 10.5.2.27 NCC Permitted */ + de_rr_pow_cmd, /* 10.5.2.28 Power Command */ + de_rr_pow_cmd_and_acc_type, /* 10.5.2.28a Power Command and access type */ + de_rr_rach_ctrl_param, /* [3] 10.5.2.29 RACH Control Parameters */ + de_rr_req_ref, /* [3] 10.5.2.30 Request Reference */ + de_rr_cause, /* 10.5.2.31 RR Cause */ + de_rr_sync_ind, /* 10.5.2.39 Synchronization Indication */ + de_rr_si1_rest_oct, /* [3] 10.5.2.32 SI1 Rest Octets */ +/* [3] 10.5.2.33 SI 2bis Rest Octets */ + de_rr_si2ter_rest_oct, /* [3] 10.5.2.33a SI 2ter Rest Octets */ + de_rr_si2quater_rest_oct, /* [3] 10.5.2.33b SI 2quater Rest Octets */ + de_rr_si3_rest_oct, /* [3] 10.5.2.34 SI3 Rest Octets */ + de_rr_si4_rest_oct, /* [3] 10.5.2.35 SI4 Rest Octets */ + de_rr_si6_rest_oct, /* [3] 10.5.2.35b SI6 Rest Octets */ +/* [3] 10.5.2.36 SI 7 Rest Octets + * [3] 10.5.2.37 SI 8 Rest Octets + * [3] 10.5.2.37a SI 9 Rest Octets + */ + de_rr_si13_rest_oct, /* [3] 10.5.2.37a SI13 Rest Octets */ +/* [3] 10.5.2.37c (void) + * [3] 10.5.2.37d (void) + * [3] 10.5.2.37e SI 16 Rest Octets + * [3] 10.5.2.37f SI 17 Rest Octets + * [3] 10.5.2.37g SI 19 Rest Octets + * [3] 10.5.2.37h SI 18 Rest Octets + * [3] 10.5.2.37i SI 20 Rest Octets */ + de_rr_starting_time, /* [3] 10.5.2.38 Starting Time */ + de_rr_timing_adv, /* [3] 10.5.2.40 Timing Advance */ + de_rr_time_diff, /* [3] 10.5.2.41 Time Difference */ + de_rr_tlli, /* [3] 10.5.2.41a TLLI */ + de_rr_tmsi_ptmsi, /* [3] 10.5.2.42 TMSI/P-TMSI */ + de_rr_vgcs_tar_mode_ind, /* [3] 10.5.2.42a VGCS target mode Indication */ + /* Pos 40 */ + de_rr_vgcs_cip_par, /* [3] 10.5.2.42b VGCS Ciphering Parameters */ + de_rr_wait_ind, /* [3] 10.5.2.43 Wait Indication */ +/* [3] 10.5.2.44 SI10 rest octets $(ASCI)$ */ + de_rr_ext_meas_result, /* [3] 10.5.2.45 Extended Measurement Results */ + de_rr_ext_meas_freq_list, /* [3] 10.5.2.46 Extended Measurement Frequency List */ + de_rr_sus_cau, /* [3] 10.5.2.47 Suspension Cause */ + de_rr_apdu_id, /* [3] 10.5.2.48 APDU ID */ + de_rr_apdu_flags, /* [3] 10.5.2.49 APDU Flags */ + de_rr_apdu_data, /* [3] 10.5.2.50 APDU Data */ + de_rr_ho_to_utran_cmd, /* [3] 10.5.2.51 Handover To UTRAN Command */ +/* [3] 10.5.2.52 Handover To cdma2000 Command + * [3] 10.5.2.53 (void) + * [3] 10.5.2.54 (void) + * [3] 10.5.2.55 (void) + * [3] 10.5.2.56 3G Target Cell + * 10.5.2.57 Service Support */ + de_rr_serv_sup, /* 10.5.2.57 Service Support */ +/* + * 10.5.2.58 MBMS p-t-m Channel Description + */ + de_rr_ded_serv_inf, /* [3] 10.5.2.59 Dedicated Service Information */ +/* + * 10.5.2.60 MPRACH Description + * 10.5.2.61 Restriction Timer + * 10.5.2.62 MBMS Session Identity + * 10.5.2.63 Reduced group or broadcast call reference + * 10.5.2.64 Talker Priority status + * 10.5.2.65 Talker Identity + * 10.5.2.66 Token + * 10.5.2.67 PS Cause + * 10.5.2.68 VGCS AMR Configuration + */ + de_rr_carrier_ind, /* 10.5.2.69 Carrier Indication */ + NULL, /* NONE */ +}; + +/* MESSAGE FUNCTIONS */ + +/* + * 9.1.1 Additional Assignment + */ +static void +dtap_rr_add_ass(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + /* Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x72, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + + /* Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7c, GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * 9.1.2 Assignment command + */ +static void +dtap_rr_ass_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, after time"); + + /* Power Command 10.5.2.28 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_POW_CMD, NULL); + + /* 05 Frequency List 10.5.2.13 C TLV 4-132 */ + ELEM_OPT_TLV(0x05, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, after time"); + + /* 62 Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62, GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* 10 Multislot Allocation 10.5.2.21b C TLV 3-12 */ + ELEM_OPT_TLV(0x10,GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, " - Description of the multislot configuration"); + + /* 63 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of the First Channel(Channel Set 1)"); + + /* 11 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* 13 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* 14 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* 15 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* 16 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* 17 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* 18 Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + /* 64 Channel Description 10.5.2.5 O TV 4 */ + ELEM_OPT_TV(0x64,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, "Description of the Second Channel, after time"); + + /* 66 Channel Mode 2 10.5.2.7 O TV 2 */ + ELEM_OPT_TV(0x66,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE2, " - Mode of the Second Channel"); + + /* 72 Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x72,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, after time"); + + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* 19 Frequency List 10.5.2.13 C TLV 4-132 */ + ELEM_OPT_TLV(0x19, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, before time"); + + /* 1C Channel Description 2 10.5.2.5a O TV 4 */ + ELEM_OPT_TV(0x1c,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, before time"); + + /* 1D Channel Description 10.5.2.5 O TV 4 */ + ELEM_OPT_TV(0x1d,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, before time"); + + /* 1E Frequency channel sequence 10.5.2.12 C TV 10 */ + ELEM_OPT_TV(0x1e,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency channel sequence before time"); + + /* 21 Mobile Allocation 10.5.2.21 C TLV 3-10 */ + ELEM_OPT_TLV(0x21,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, before time"); + + /* 9- Cipher Mode Setting 10.5.2.9 O TV 1 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, NULL); + /* 01 VGCS target mode Indication VGCS target mode Indication 10.5.2.42a O TLV 3 */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* 03 Multi-Rate configuration, MultiRate configuration 10.5.2.21aa O TLV 4-8 */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + /* 04 VGCS Ciphering Parameters VGCS Ciphering Parameters 10.5.2.42b O TLV 3-15 */ + ELEM_OPT_TLV(0x04,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_CIP_PAR, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.3 Assignment complete + */ +static void +dtap_rr_ass_comp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.4 Assignment failure + */ +static void +dtap_rr_ass_fail(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.5 Channel Mode Modify + */ +static void +dtap_rr_ch_mode_mod(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, NULL); + + /* Channel Mode 10.5.2.6 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, NULL); + + /* 01 VGCS target mode Indication VGCS target mode Indication 10.5.2.42a O TLV 3 */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* 03 Multi-Rate configuration, MultiRate configuration 10.5.2.21aa O TLV 4-8 */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.6 Channel Mode Modify Acknowledge + */ +static void +dtap_rr_ch_mode_mod_ack(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 2 10.5.2.5a M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, NULL); + + /* Channel Mode 10.5.2.6 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.7 Channel Release + */ +static void +dtap_rr_ch_rel(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + /* 73 BA Range BA Range 10.5.2.1a O TLV 6-7 */ + ELEM_OPT_TLV(0x73, GSM_A_PDU_TYPE_RR, DE_RR_BA_RANGE, NULL); + + /* 74 Group Channel Description Group Channel Description 10.5.2.14b O TLV 5-13 */ + /* ELEM_OPT_TLV(0x74, GSM_A_PDU_TYPE_RR, DE_GRP_CH_DESC, NULL); */ + + /* 8x Group Cipher Key Number Group Cipher Key Number 10.5.1.10 C TV 1 */ + /* ELEM_OPT_TV_SHORT(0x80, GSM_A_PDU_TYPE_RR, DE_GRP_CIP_KEY_NUM, NULL); */ + + /* Cx GPRS Resumption GPRS Resumption 10.5.2.14c O TV 1 */ + ELEM_OPT_TV_SHORT(0xC0, GSM_A_PDU_TYPE_RR, DE_RR_GPRS_RESUMPTION, NULL); + + /* 75 BA List Pref BA List Pref 10.5.2.1c O TLV 3-? */ + ELEM_OPT_TLV(0x75, GSM_A_PDU_TYPE_RR, DE_RR_BA_LIST_PREF, NULL); + + /* 76 UTRAN Freq List 10.5.2.1d O TLV 3-? */ + ELEM_OPT_TLV(0x76, GSM_A_PDU_TYPE_RR, DE_RR_UTRAN_FREQ_LIST, NULL); + + /* 62 Cell Channel Description Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62, GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* 62 Cell selection indicator after release of all TCH and SDCCH 10.5.2.1e O TLV 4-? */ + ELEM_OPT_TLV(0x77, GSM_A_PDU_TYPE_RR, DE_RR_CELL_SELECT_INDIC, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.8 Channel Request + */ +/* This message is NOT follow the basic format, and is only found on RACH - ignored here */ + +/* + * 9.1.9 Ciphering Mode Command + */ +static void +dtap_rr_cip_mode_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Ciphering Mode Setting 10.5.2.9 M V 0.5 */ + /* Cipher Response 10.5.2.10 M V 0.5 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, DE_RR_CIP_MODE_RESP); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} +/* + * 9.1.10 Ciphering Mode Complete + */ +void +dtap_rr_cip_mode_cpte(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mobile Equipment Identity 10.5.1.4 O TLV */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, "Mobile Equipment Identity"); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.11 Classmark change + */ +static void +dtap_rr_mm_cm_change(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mobile Station Classmark 2 10.5.1.6 M LV 4 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MS_CM_2, NULL); + /* 20 Mobile Station Classmark 3 10.5.1.7 C TLV 3-34 */ + ELEM_OPT_TLV(0x20, GSM_A_PDU_TYPE_COMMON, DE_MS_CM_3, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.11 UTRAN Classmark Change + */ +static void +dtap_rr_utran_classmark_change(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* UTRAN Classmark 10.5.2.7a M LV 2-? */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_UTRAN_CM, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * 9.1.12 Classmark enquiry + */ +static void +dtap_rr_cm_enq(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* 10 Classmark Enquiry Mask 10.5.2.7c O TLV 3 */ + ELEM_OPT_TLV(0x10, GSM_A_PDU_TYPE_RR, DE_RR_CM_ENQ_MASK, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.12b Configuration change command + */ +static void +dtap_rr_conf_change_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Multislot Allocation 10.5.2.21b M LV 2-11 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, NULL); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 1"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* Channel Mode 10.5.2.6 O TV 2 */ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); +} + +/* + * 9.1.12c Configuration change acknowledge + */ +/* empty message */ + +/* + * 9.1.12d Configuration change reject + */ +static void +dtap_rr_conf_change_rej(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + + +/* + * 9.1.13 Frequency Redefinition + */ +static void +dtap_rr_freq_redef(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + + /* Starting Time 10.5.2.38 M V 2 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* Cell Channel Description 10.5.2.1b O TV 17 */ + ELEM_OPT_TV(0x62,GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* Carrier Indication 10.5.2.69 O TV 1 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CARRIER_IND,NULL); + + /* Mobile Allocation 10.5.2.21 O TLV 1-9 */ + ELEM_OPT_TLV(0x11, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation C2"); + + /* Channel Description 3 10.5.2.5c O TV 3 */ + ELEM_OPT_TV(0x12,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC3, " - Channel Description C2"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); +} + +/* + * 9.1.13b GPRS suspension request + */ +static void +dtap_rr_gprs_sus_req(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* TLLI 10.5.2.41a M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TLLI, NULL); + + /* Routeing Area Identification 10.5.5.15 M V 6 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_GM, DE_RAI, NULL); + /* Suspension cause 10.5.2.47 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SUS_CAU, NULL); + + /* 01 Service Support 10.5.2.57 O TV 2 */ + ELEM_OPT_TV_SHORT(0x01,GSM_A_PDU_TYPE_RR, DE_RR_SERV_SUP,NULL); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.14 Handover Access + */ +/* This message does NOT follow the basic format, and is only found on DCH during initial handover access */ + +/* 3GPP TS 24.008 version 4.7.0 Release 4 + * [3] 9.1.15 + */ +void +dtap_rr_ho_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Mandatory Elemets + * Cell description 10.5.2.2 + */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_DSC, NULL); + + /* Description of the first channel,after time + * Channel Description 2 10.5.2.5a + */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the first channel, after time"); + + /* Handover Reference 10.5.2.15 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_HO_REF, NULL); + + /* Power Command and Access type 10.5.2.28a */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_POW_CMD_AND_ACC_TYPE, NULL); + + /* optional elements */ + + /* Synchronization Indication 10.5.2.39 */ + ELEM_OPT_TV_SHORT(0xD0,GSM_A_PDU_TYPE_RR, DE_RR_SYNC_IND,NULL); + + /* Frequency Short List 10.5.2.14 */ + ELEM_OPT_TV(0x02,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_SHORT_LIST," - Frequency Short List, after time"); + + /* Frequency List 10.5.2.13 */ + ELEM_OPT_TLV(0x05, GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, after time"); + + /* Cell Channel Description 10.5.2.1b */ + ELEM_OPT_TV(0x62,GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + /* Multislot Allocation 10.5.2.21b */ + ELEM_OPT_TLV(0x10,GSM_A_PDU_TYPE_RR, DE_RR_MULT_ALL, NULL); + + /* Mode of the First Channel(Channel Set 1)) Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x63,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of the First Channel(Channel Set 1))"); + + /* Mode of Channel Set 2 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x11,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 2"); + + /* Mode of Channel Set 3 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x13,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 3"); + + /* Mode of Channel Set 4 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x14,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 4"); + + /* Mode of Channel Set 5 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x15,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 5"); + + /* Mode of Channel Set 6 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x16,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 6"); + + /* Mode of Channel Set 7 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x17,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 7"); + + /* Mode of Channel Set 8 Channel Mode 10.5.2.6*/ + ELEM_OPT_TV(0x18,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE, " - Mode of Channel Set 8"); + + /* Description of the Second Channel, after time, Channel Description 10.5.2.5 */ + ELEM_OPT_TV(0x64,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, after time"); + + /* Mode of the Second Channel, Channel Mode 2 10.5.2.7 */ + ELEM_OPT_TV(0x66,GSM_A_PDU_TYPE_RR, DE_RR_CH_MODE2, " - Mode of the Second Channel"); + + /* Frequency Channel Sequence, after time, Frequency Channel Sequence 10.5.2.12 */ + ELEM_OPT_TV(0x69,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency Channel Sequence, after time"); + + /* Mobile Allocation, after time, Mobile Allocation 10.5.2.21 */ + ELEM_OPT_TLV(0x72,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, after time"); + + /* Starting Time 10.5.2.38 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + + /* Real Time Difference, Time Difference 10.5.2.41 */ + ELEM_OPT_TV(0x7B,GSM_A_PDU_TYPE_RR, DE_RR_TIME_DIFF, " - Real Time Difference"); + + /* Timing Advance, Timing Advance 10.5.2.40 */ + ELEM_OPT_TV(0x7D,GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + + /* Frequency Short List, before time, Frequency Short List 10.5.2.14 */ + ELEM_OPT_TV(0x12,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_SHORT_LIST, " - Frequency Short List, before time"); + + /* Frequency List, before time, Frequency List 10.5.2.13 */ + ELEM_OPT_TLV(0x19,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_LIST, " - Frequency List, before time"); + + /* Description of the First Channel, before time, Channel Description 2 10.5.2.5a*/ + ELEM_OPT_TV(0x1c,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC2, " - Description of the First Channel, before time"); + + /* Description of the Second Channel, before time, Channel Description 10.5.2.5*/ + ELEM_OPT_TV(0x1d,GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Description of the Second Channel, before time"); + + /* Frequency channel sequence before time, Frequency channel sequence 10.5.2.12*/ + ELEM_OPT_TV(0x1e,GSM_A_PDU_TYPE_RR, DE_RR_FREQ_CH_SEQ, " - Frequency channel sequence before time"); + + /* Mobile Allocation, before time, Mobile Allocation 10.5.2.21 */ + ELEM_OPT_TLV(0x21,GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - Mobile Allocation, before time"); + + /* Cipher Mode Setting, Cipher Mode Setting 10.5.2.9 */ + ELEM_OPT_TV_SHORT(0x90,GSM_A_PDU_TYPE_RR, DE_RR_CIP_MODE_SET, NULL); + + /* VGCS target mode Indication, VGCS target mode Indication 10.5.2.42a */ + ELEM_OPT_TLV(0x01,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_TAR_MODE_IND, NULL); + + /* Multi-Rate configuration, MultiRate configuration 10.5.2.21a */ + ELEM_OPT_TLV(0x03,GSM_A_PDU_TYPE_RR, DE_RR_MULTIRATE_CONF, NULL); + + /* Dynamic ARFCN Mapping, Dynamic ARFCN Mapping 10.5.2.11b */ + ELEM_OPT_TLV(0x76,GSM_A_PDU_TYPE_RR, DE_RR_DYN_ARFCN_MAP, NULL); + + /* VGCS Ciphering Parameters, VGCS Ciphering Parameters 10.5.2.42b */ + ELEM_OPT_TLV(0x04,GSM_A_PDU_TYPE_RR, DE_RR_VGCS_CIP_PAR, NULL); + + /* Dedicated Service Information, Dedicated Service Information 10.5.2.59 */ + ELEM_OPT_TV(0x51,GSM_A_PDU_TYPE_RR, DE_RR_DED_SERV_INF, NULL); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.15a Inter System To UTRAN Handover Command + */ +static void +dtap_rr_inter_syst_to_utran_ho_cmd(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* UTRAN Classmark 10.5.2.51 M LV 2-? */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_HO_TO_UTRAN_CMD, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* 3GPP TS 24.008 version 4.7.0 Release 4 + * [3] 9.1.16 + */ +static void +dtap_rr_ho_cpte(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + /* 77 Mobile Observed Time Difference Mobile Time Difference 10.5.2.21a */ + ELEM_OPT_TLV(0x77,GSM_A_PDU_TYPE_RR, DE_RR_MOB_TIME_DIFF, " - Mobile Observed Time Difference"); + + EXTRANEOUS_DATA_CHECK(len, curr_offset - offset); + +} + +/* + * 9.1.17 Handover failure + */ +static void +dtap_rr_ho_fail(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Cause RR Cause 10.5.2.31 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); + +} + +/* + * 9.1.18 Immediate assignment See 3GPP TS 44.018 + */ +static void +dtap_rr_imm_ass(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + guint8 oct; + + curr_offset = offset; + curr_len = len; + + oct = tvb_get_guint8(tvb, curr_offset); + + /* NOTE: The order of the mandatory information elements should be chosen so that + * information elements with 1/2 octet of content (type 1) go together in succession. + * The first type 1 information element occupies bits 1 to 4 of octet N, + * the second bits 5 to 8 of octet N, the third bits 1 to 4 of octet N+1 etc. + * If the number of type 1 information elements is odd then bits 5 to 8 of the last octet + * occupied by these information elements should be treated as spare bits, + * i.e. coded with a "0" in each. + */ + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Dedicated mode or TBF 10.5.2.25b M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_DED_MOD_OR_TBF); + + if((oct&0x10) == 0){ + /* Channel Description 10.5.2.5 C V 3m */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + }else{ + /* Packet Channel Description 10.5.2.25a C V 3 + * If the Dedicated mode or TBF IE indicates that the message assigns a Temporary Block Flow (TBF), + * the mobile station shall consider this information element present in the message. + * If the Dedicated mode or TBF IE indicates that this message is the first of two in a two-message + * assignment of an uplink or downlink TBF, the mobile station shall ignore the contents + * of this information element and regard it as an unnecessary IE. + */ + if((oct&0x04) == 0){ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_PACKET_CH_DESC, NULL); + } + } + /* Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, NULL); + + /* Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + /* IA Rest Octets 10.5.2.16 M V 0-11 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IA_REST_OCT, NULL); + +} + +/* + * 9.1.19 Immediate assignment extended + */ +static void +dtap_rr_imm_ass_ext(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Spare Half Octet 10.5.1.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_SPARE_NIBBLE); + /* Channel Description 1 Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Channel Description 1"); + /* Request Reference 1 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 1"); + /* Timing Advance 1 Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, " - Timing Advance 1"); + /* Channel Description 2 Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - Channel Description 2"); + /* Request Reference 2 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 2"); + /* Timing Advance 2 Timing Advance 10.5.2.40 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, " - Timing Advance 2"); + /* Mobile Allocation 10.5.2.21 M LV 1-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, NULL); + /* 7C Starting Time 10.5.2.38 O TV 3 */ + ELEM_OPT_TV(0x7C,GSM_A_PDU_TYPE_RR, DE_RR_STARTING_TIME, NULL); + /* IAX Rest Octets 10.5.2.18 M V 0-4 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IAX_REST_OCT, NULL); + +} + +/* + * 9.1.20 Immediate assignment reject + */ +static void +dtap_rr_imm_ass_rej(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Page Mode 10.5.2.26 M V 1/2 */ + /* Spare Half Octet 10.5.1.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_SPARE_NIBBLE); + /* Request Reference 1 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 1"); + /* Wait Indication 1 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 1"); + /* Request Reference 2 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 2"); + /* Wait Indication 2 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 2"); + /* Request Reference 3 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 3"); + /* Wait Indication 3 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 3"); + /* Request Reference 4 Request Reference 10.5.2.30 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_REQ_REF, " - Request Reference 4"); + /* Wait Indication 4 Wait Indication 10.5.2.43 M V 1 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_WAIT_IND, " - Wait Indication 4"); + /* IAR Rest Octets 10.5.2.19 M V 3 */ + if(tvb_length_remaining(tvb,curr_offset) > 0) + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_IAR_REST_OCT, NULL); + +} + +/* + * 9.1.21 Measurement report + */ +static void +dtap_rr_meas_rep(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Measurement Results 10.5.2.20 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_MEAS_RES, NULL); +} + +/* + * 9.1.22 Paging Request Type 1 + */ +static void +dtap_rr_paging_req_type_1(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR Mobile Identity 10.5.1.4 M LV 2-9 */ + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 1"); + + /* RR Mobile Identity 10.5.1.4 O TLV 3-10 */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 2"); + + /* RR P1 Rest Octets 10.5.2.23 M V 0-17 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P1_REST_OCT, NULL); + +} + +/* + * 9.1.23 Paging Request Type 2 + */ +static void +dtap_rr_paging_req_type_2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 1"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 2"); + + /* RR Mobile Identity 10.5.1.4 O TLV 3-10 */ + ELEM_OPT_TLV(0x17, GSM_A_PDU_TYPE_COMMON, DE_MID, " - Mobile Identity 3"); + + /* RR P2 Rest Octets 10.5.2.24 M V 1-11 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P2_REST_OCT, NULL); + +} + +/* + * 9.1.24 Paging Request Type 3 + */ +static void +dtap_rr_paging_req_type_3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* RR Page Mode 10.5.2.26 M V 1/2 */ + /* RR Channel Needed 10.5.2.8 M V 1/2 */ + ELEM_MAND_VV_SHORT(GSM_A_PDU_TYPE_RR, DE_RR_PAGE_MODE, DE_RR_CHNL_NEEDED); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 1"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 2"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 3"); + + /* RR TMSI/P-TMSI 10.5.2.42 M V 4 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TMSI_PTMSI, " - Mobile Identity 4"); + + /* RR P3 Rest Octets 10.5.2.25 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_P3_REST_OCT, NULL); + +} + +/* + * [4] 9.1.25 Paging response + */ +static void +dtap_rr_paging_resp(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + guint8 oct; + proto_tree *subtree; + proto_item *item; + + curr_offset = offset; + curr_len = len; + + /* + * special dissection for Cipher Key Sequence Number + */ + oct = tvb_get_guint8(tvb, curr_offset); + + other_decode_bitfield_value(a_bigbuf, oct, 0xf0, 8); + proto_tree_add_text(tree, + tvb, curr_offset, 1, + "%s : Spare", + a_bigbuf); + + item = + proto_tree_add_text(tree, + tvb, curr_offset, 1, "%s", + gsm_common_elem_strings[DE_CIPH_KEY_SEQ_NUM].strptr); + + subtree = proto_item_add_subtree(item, ett_gsm_common_elem[DE_CIPH_KEY_SEQ_NUM]); + + other_decode_bitfield_value(a_bigbuf, oct, 0x08, 8); + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Spare", + a_bigbuf); + + other_decode_bitfield_value(a_bigbuf, oct, 0x07, 8); + + switch (oct & 0x07) + { + case 0x07: + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Ciphering Key Sequence Number: No key is available", + a_bigbuf); + break; + + default: + proto_tree_add_text(subtree, + tvb, curr_offset, 1, + "%s : Ciphering Key Sequence Number: %u", + a_bigbuf, + oct & 0x07); + break; + } + + curr_offset++; + curr_len--; + + if ((signed)curr_len <= 0) return; + + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MS_CM_2, NULL); + + ELEM_MAND_LV(GSM_A_PDU_TYPE_COMMON, DE_MID, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.26 Partial Release + */ +static void +dtap_rr_partial_rel(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Channel Description 10.5.2.5 M V 3 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.27 Partial Release Complete + */ +/* empty message */ + +/* + * [4] 9.1.28 Physical Information + */ +static void +dtap_rr_phy_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_TIMING_ADV, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.29 + */ +static void +dtap_rr_rr_status(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CAUSE, NULL); + + EXTRANEOUS_DATA_CHECK(curr_len, 0); +} + +/* + * [4] 9.1.31 + */ +static void +dtap_rr_sys_info_1(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_CH_DSC, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI1_REST_OCT, NULL); +} + +/* + * [4] 9.1.32 + */ +static void +dtap_rr_sys_info_2(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NCC_PERM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); +} + +/* + * [4] 9.1.33 + */ +static void +dtap_rr_sys_info_2bis(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - Extended BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); +} + +/* + * [4] 9.1.34 + */ +static void +dtap_rr_sys_info_2ter(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC2, " - Extended BCCH Frequency List"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI2TER_REST_OCT, NULL); +} + +/* + * [4] 9.1.34a + */ +static void +dtap_rr_sys_info_2quater(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI2QUATER_REST_OCT, NULL); +} + +/* + * [4] 9.1.35 + */ +static void +dtap_rr_sys_info_3(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_CELL_ID, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CTRL_CH_DESC, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_OPT_BCCH, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_SEL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI3_REST_OCT, NULL); +} + +/* + * [4] 9.1.36 + */ +static void +dtap_rr_sys_info_4(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_SEL_PARAM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_RACH_CTRL_PARAM, NULL); + + ELEM_OPT_TV(0x64, GSM_A_PDU_TYPE_RR, DE_RR_CH_DSC, " - CBCH"); + + ELEM_OPT_TLV(0x72, GSM_A_PDU_TYPE_RR, DE_RR_MOB_ALL, " - CBCH"); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI4_REST_OCT, NULL); +} + +/* + * [4] 9.1.37 + */ +static void +dtap_rr_sys_info_5(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - BCCH Frequency List"); +} + +/* + * [4] 9.1.38 + */ +static void +dtap_rr_sys_info_5bis(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC, " - Extended BCCH Frequency List"); +} + +/* + * [4] 9.1.39 + */ +static void +dtap_rr_sys_info_5ter(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NEIGH_CELL_DESC2, " - Extended BCCH Frequency List"); +} + +/* + * [4] 9.1.40 + */ +static void +dtap_rr_sys_info_6(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_CELL_ID, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_COMMON, DE_LAI, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_CELL_OPT_SACCH, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_NCC_PERM, NULL); + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI6_REST_OCT, NULL); +} + +/* + * [4] 9.1.43a + */ +static void +dtap_rr_sys_info_13(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_SI13_REST_OCT, NULL); +} + +/* + * [4] 9.1.51 Extended Measurement Order + */ +static void +dtap_rr_ext_meas_order(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Extended Measurement Frequency List 10.5.2.46 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_EXT_MEAS_FREQ_LIST, NULL); +} + +/* + * [4] 9.1.52 Extended Measurement Report + */ +static void +dtap_rr_ext_meas_report(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + /* Extended Measurement Result 10.5.2.45 M V 16 */ + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_EXT_MEAS_RESULT, NULL); +} + +/* + * 9.1.53 Application Information + */ +static void +dtap_rr_app_inf(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + curr_offset = offset; + curr_len = len; + + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_ID, NULL); + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_APDU_FLAGS, NULL); + ELEM_MAND_LV(GSM_A_PDU_TYPE_RR, DE_RR_APDU_DATA, NULL); +} + +/* + * [4] 9.1.54 Measurement Information + */ +static const value_string gsm_a_rr_3g_wait_vals[] = { + { 0, "1 instance that contain 3G Neighbour Cell Description shall be received"}, + { 1, "2 instances that contain 3G Neighbour Cell Description shall be received"}, + { 2, "3 instances that contain 3G Neighbour Cell Description shall be received"}, + { 3, "4 instances that contain 3G Neighbour Cell Description shall be received"}, + { 4, "5 instances that contain 3G Neighbour Cell Description shall be received"}, + { 5, "6 instances that contain 3G Neighbour Cell Description shall be received"}, + { 6, "7 instances that contain 3G Neighbour Cell Description shall be received"}, + { 7, "8 instances that contain 3G Neighbour Cell Description shall be received"}, + { 0, NULL } +}; + + +static void +sacch_rr_meas_info(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len _U_) +{ + proto_tree *subtree = NULL, *subtree2 = NULL; + proto_item *item, *item2; + guint32 curr_offset; + gint bit_offset, bit_offset_sav, bit_offset_sav2; + guint8 value, idx; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_3g_ba_ind, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mp_change_mark, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mi_index, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_mi_count, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_pwrc, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_gsm_report_type, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_reporting_rate, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset +=1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_invalid_bsic_reporting, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset +=1; + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Real Time Difference Description", "Present", "Not Present")) + { /* Real Time Difference Description */ + bit_offset += de_rr_rtd_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_RTD_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "BSIC Description", "Present", "Not Present")) + { /* BSIC Description */ + bit_offset += de_rr_bsic_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_BSIC_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Report Priority Description", "Present", "Not Present")) + { /* Report Priority Description */ + bit_offset += de_rr_report_priority_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_REPORT_PRIO_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Measurement Parameters Description", "Present", "Not Present")) + { /* Measurement Parameters Description */ + bit_offset += de_rr_meas_param_desc(tvb, tree, bit_offset, DE_RR_REST_OCTETS_MEAS_PARAM_DESC); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "future extensions of the 2G parameters", "Present", "Not Present")) + { /* used for future extensions of the 2G parameters */ + value = tvb_get_bits8(tvb,bit_offset,8); + bit_offset += 8 + value + 1; + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "3G Neighbour Cell Description", "Present", "Not Present")) + { /* 3G Neighbour Cell Description */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_NEIGH_CELL_DESC]); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Wait", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_wait, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Index Start 3G", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_index_start_3g, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Absolute Index Start EMR", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_absolute_index_start_emr, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN FDD Description", "Present", "Not Present")) + { /* UTRAN FDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_fdd_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "UTRAN TDD Description", "Present", "Not Present")) + { /* UTRAN TDD Description */ + bit_offset += de_rr_si2quater_meas_info_utran_tdd_desc(tvb, subtree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Description", "Present", "Not Present")) + { /* CDMA2000 Description */ + bit_offset_sav2 = bit_offset; + item2 = proto_tree_add_text(subtree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_CDMA2000_DESC].strptr); + subtree2 = proto_item_add_subtree(item2, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_CDMA2000_DESC]); + bit_offset += 16; /* cdma2000 frequency band + cdma2000 frequency */ + idx = tvb_get_bits8(tvb,bit_offset,5); /* number_cdma2000_cells */ + bit_offset += 5; + while (idx) + { + bit_offset += 9; /* Pilot PN offset */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "additional information for 3X Auxiliary Pilot", "Present", "Not Present")) + { + value = tvb_get_bits8(tvb,bit_offset,3); + switch (value) + { + case 0: + bit_offset += 5; /* TD_MODE + TD_POWER_LEVEL */ + break; + case 1: + bit_offset += 2; /* QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_A + bit(val(WALSH_LEN_A)+6) */ + break; + case 2: + bit_offset += 2; /* QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_B + bit(val(WALSH_LEN_B)+6) */ + bit_offset += 4; /* AUX_TD_POWER_LEVEL + TD_MODE */ + break; + case 3: + bit_offset += 8; /* SR3_PRIM_PILOT + SR3_PILOT_POWER1 + SR3_PILOT_POWER2 */ + break; + case 6: + bit_offset += 10; /* SR3_PRIM_PILOT + SR3_PILOT_POWER1 + SR3_PILOT_POWER2 + QOF */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LEN_C + bit(val(WALSH_LEN_C)+6) */ + if (tvb_get_bits8(tvb,bit_offset,1)) + { + bit_offset += 3; /* 1 + QOF1 */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LENGTH1 + bit(val(WALSH_LENGTH1)+6) */ + } + else + bit_offset += 1; + if (tvb_get_bits8(tvb,bit_offset,1)) + { + bit_offset += 3; /* 1 + QOF2 */ + bit_offset += tvb_get_bits8(tvb,bit_offset,3) + 3 + 6; /* WALSH_LENGTH2 + bit(val(WALSH_LENGTH2)+6) */ + } + else + bit_offset += 1; + break; + default: + /* decoding sequence is unknown ! */ + return; + } + } + idx -= 1; + } + proto_tree_add_text(subtree2,tvb, bit_offset_sav2>>3, ((bit_offset-bit_offset_sav2)>>3)+1,"Data(Not decoded)"); + proto_item_set_len(item2,((bit_offset-bit_offset_sav2)>>3)+1); + } + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Measurement Parameters Description", "Present", "Not Present")) + { /* 3G Measurement Parameters Description */ + guint8 reporting_quant = 0; + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_3G_MEAS_PARAM_DESC]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_qsearch_c, tvb, bit_offset, 4, ENC_BIG_ENDIAN); + bit_offset += 4; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_3g_search_prio, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_rep_quant, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + reporting_quant = gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Reporting Quantity", "Ec/No", "RSCP"); + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "FDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_fdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Multirat Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_multirat_reporting, tvb, bit_offset, 2, ENC_BIG_ENDIAN); + bit_offset += 2; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "TDD Reporting Offset", "Present", "Not Present")) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + if (reporting_quant == 0) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_threshold_rscp, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + else + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_tdd_reporting_threshold_ecn0, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + } + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Multirat Reporting", "Present", "Not Present")) + bit_offset += 2; /* CDMA2000 Multirat Reporting */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "CDMA2000 Reporting Threshold", "Present", "Not Present")) + bit_offset += 6; /* CDMA2000 Reporting Offset + CDMA2000 Reporting Threshold */ + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-5", "Present", "Not present")) + { /* Additions in Rel-5 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Additional Measurement Parameters Description 2", "Present", "Not Present")) + { /* 3G Additional Measurement Parameters Description 2 */ + bit_offset += de_rr_3g_add_meas_param_desc2(tvb, tree, bit_offset); + } + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-7", "Present", "Not present")) + { /* Additions in Rel-7 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "700 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_700_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_700_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "810 Reporting", "Present", "Not Present")) + { + proto_tree_add_bits_item(tree, hf_gsm_a_rr_810_reporting_offset, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_810_reporting_threshold, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + } + /* Additions in Rel-8 */ + if (((curr_offset + len)<<3) - bit_offset > 0) + { + /* There is still room left in the Rest Octets IE */ + if (gsm_rr_csn_HL_flag(tvb,tree,0,bit_offset++,"Additions in Rel-8", "Present", "Not present")) + { /* Additions in Rel-8 */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "3G Supplementary Parameters Description", "Present", "Not Present")) + { + bit_offset += de_rr_3g_supplementary_param_desc_mi(tvb, tree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN Parameters Description", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_param_desc_mi(tvb, tree, bit_offset); + } + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN CSG Description", "Present", "Not Present")) + { + bit_offset += de_rr_eutran_csg_desc_mi(tvb, tree, bit_offset); + } + } + } + } + } + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); +} + +static guint32 +sacch_rr_eutran_meas_report(tvbuff_t *tvb, proto_tree *tree, guint32 bit_offset, guint len_in_bit _U_) +{ + proto_tree *subtree; + proto_item *item; + gint curr_bit_offset; + gint8 n_eutran; + + curr_bit_offset = bit_offset; + + item = proto_tree_add_text(tree, tvb, curr_bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_EUTRAN_MEASUREMENT_REPORT]); + + n_eutran = tvb_get_bits8(tvb,curr_bit_offset,2); + n_eutran += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_n_eutran, tvb, curr_bit_offset, 2, ENC_BIG_ENDIAN); + curr_bit_offset += 2; + + while ( (n_eutran > 0) && (curr_bit_offset - bit_offset < len_in_bit) ) + { + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_freq_idx, tvb, curr_bit_offset, 3, ENC_BIG_ENDIAN); + curr_bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_cell_id, tvb, curr_bit_offset, 9, ENC_BIG_ENDIAN); + curr_bit_offset += 9; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_eutran_mr_rpt_quantity, tvb, curr_bit_offset, 6, ENC_BIG_ENDIAN); + curr_bit_offset += 6; + n_eutran -= 1; + } + + return curr_bit_offset - bit_offset; +} + +/* + * [4] 9.1.55 Enhanced Measurement Information + */ +static const true_false_string gsm_a_rr_bsic_seen_value = { + "One Cell or more with invalid BSIC and allowed NCC part of BSIC is seen", + "No cell with invalid BSIC and allowed NCC part of BSIC is seen" +}; + +static const true_false_string gsm_a_rr_scale_value = { + "+10 dB", + "0 dB" +}; + +static const value_string gsm_a_rr_mean_bep_gmsk_vals[] = { + { 0, "log10(BEP) > -0.60"}, + { 1, "-0.70 < log10(BEP) < -0.60"}, + { 2, "-0.80 < log10(BEP) < -0.70"}, + { 3, "-0.90 < log10(BEP) < -0.80"}, + { 4, "-1.00 < log10(BEP) < -0.90"}, + { 5, "-1.10 < log10(BEP) < -1.00"}, + { 6, "-1.20 < log10(BEP) < -1.10"}, + { 7, "-1.30 < log10(BEP) < -1.20"}, + { 8, "-1.40 < log10(BEP) < -1.30"}, + { 9, "-1.50 < log10(BEP) < -1.40"}, + { 10, "-1.60 < log10(BEP) < -1.50"}, + { 11, "-1.70 < log10(BEP) < -1.60"}, + { 12, "-1.80 < log10(BEP) < -1.70"}, + { 13, "-1.90 < log10(BEP) < -1.80"}, + { 14, "-2.00 < log10(BEP) < -1.90"}, + { 15, "-2.10 < log10(BEP) < -2.00"}, + { 16, "-2.20 < log10(BEP) < -2.10"}, + { 17, "-2.30 < log10(BEP) < -2.20"}, + { 18, "-2.40 < log10(BEP) < -2.30"}, + { 19, "-2.50 < log10(BEP) < -2.40"}, + { 20, "-2.60 < log10(BEP) < -2.50"}, + { 21, "-2.70 < log10(BEP) < -2.60"}, + { 22, "-2.80 < log10(BEP) < -2.70"}, + { 23, "-2.90 < log10(BEP) < -2.80"}, + { 24, "-3.00 < log10(BEP) < -2.90"}, + { 25, "-3.10 < log10(BEP) < -3.00"}, + { 26, "-3.20 < log10(BEP) < -3.10"}, + { 27, "-3.30 < log10(BEP) < -3.20"}, + { 28, "-3.40 < log10(BEP) < -3.30"}, + { 29, "-3.50 < log10(BEP) < -3.40"}, + { 30, "-3.60 < log10(BEP) < -3.50"}, + { 31, "log10(BEP) < -3.60"}, + { 0, NULL } +}; + +static const value_string gsm_a_rr_cv_bep_vals[] = { + { 0, "1.75 < CV BEP < 2.00"}, + { 1, "1.50 < CV BEP < 1.75"}, + { 2, "1.25 < CV BEP < 1.50"}, + { 3, "1.00 < CV BEP < 1.25"}, + { 4, "0.75 < CV BEP < 1.00"}, + { 5, "0.50 < CV BEP < 0.75"}, + { 6, "0.25 < CV BEP < 0.50"}, + { 7, "0.00 < CV BEP < 0.25"}, + { 0, NULL } +}; + +static void +sacch_rr_enh_meas_report(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) +{ + proto_tree *subtree; + proto_item *item; + guint32 curr_offset; + gint bit_offset, bit_offset_sav; + guint8 idx; + + curr_offset = offset; + bit_offset = curr_offset << 3; + + proto_tree_add_bits_item(tree, hf_gsm_a_rr_ba_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_3g_ba_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_bsic_seen, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(tree, hf_gsm_a_rr_scale, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Serving cell data", "Present", "Not Present")) + { /* Serving cell data */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_SERVING_CELL_DATA].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_SERVING_CELL_DATA]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_dtx_used, tvb, bit_offset, 1, ENC_BIG_ENDIAN); + bit_offset += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_full_serv_cell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxqual_full_serv_cell, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mean_bep_gmsk, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_mean_cv_bep, tvb, bit_offset, 3, ENC_BIG_ENDIAN); + bit_offset += 3; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_nbr_rcvd_blocks, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + while (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Repeated Invalid BSIC Information", "Present", "Not Present")) + { /* Repeated Invalid BSIC Information */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_REPEAT_INV_BSIC_INFO]); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bcch_freq_ncell, tvb, bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_bsic_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_rxlev_ncell, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + proto_item_set_len(item,((bit_offset-bit_offset_sav)>>3)+1); + } + if (gsm_rr_csn_flag(tvb, tree, bit_offset++, "Bitmap Type Reporting", "Present", "Not Present")) + { /* Bitmap Type Reporting */ + bit_offset_sav = bit_offset; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_BITMAP_TYPE_REPORTING]); + idx = 0; + while (((guint)(bit_offset>>3) <= (offset + len)) && (idx < 96)) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Reporting Quantity", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Neighbour Cell List index: %u", idx); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_quantity, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + idx += 1; + } + } + if (gsm_rr_csn_HL_flag(tvb,tree, 0,bit_offset++,"Additions for Rel-8", "Present", "Not Present")) + { + gint8 bitmap_length; + item = proto_tree_add_text(tree, tvb, bit_offset>>3, -1, "%s", gsm_rr_rest_octets_elem_strings[DE_RR_REST_OCTETS_REPORTING_QUANTITY].strptr); + subtree = proto_item_add_subtree(item, ett_gsm_rr_rest_octets_elem[DE_RR_REST_OCTETS_REPORTING_QUANTITY]); + + bitmap_length = tvb_get_bits8(tvb,bit_offset,7); + bitmap_length += 1; + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_emr_bitmap_length, tvb, bit_offset, 7, ENC_BIG_ENDIAN); + bit_offset += 7; + + /* REPORTING_QUANTITY */ + idx = 0; + while ((guint)(bit_offset>>3) <= (offset + len) && (idx < bitmap_length) ) + { + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "Reporting Quantity", "Present", "Not Present")) + { + proto_tree_add_text(subtree, tvb, bit_offset>>3, 1, "Neighbour Cell List index: %u", idx); + proto_tree_add_bits_item(subtree, hf_gsm_a_rr_reporting_quantity, tvb, bit_offset, 6, ENC_BIG_ENDIAN); + bit_offset += 6; + } + idx += 1; + } + + /* E-UTRAN Measurement Report */ + if (gsm_rr_csn_flag(tvb, subtree, bit_offset++, "E-UTRAN Measurement Report", "Present", "Not Present")) + { + bit_offset += sacch_rr_eutran_meas_report(tvb, subtree, bit_offset, len*8-(bit_offset-offset*8)); + } + } + gsm_rr_csn_spare_bits(tree, tvb, bit_offset); +} + +#define NUM_GSM_DTAP_MSG_RR (sizeof(gsm_a_dtap_msg_rr_strings)/sizeof(value_string)) +static gint ett_gsm_dtap_msg_rr[NUM_GSM_DTAP_MSG_RR]; +static void (*dtap_msg_rr_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len) = { + NULL, /* Reserved */ + dtap_rr_add_ass, /* Additional Assignment */ + dtap_rr_imm_ass, /* 9.1.18 Immediate assignment */ + dtap_rr_imm_ass_ext, /* Immediate Assignment Extended */ + dtap_rr_imm_ass_rej, /* Immediate Assignment Reject */ + + NULL, /* DTM Assignment Failure */ + NULL, /* DTM Reject */ + NULL, /* DTM Request */ + NULL, /* Main DCCH Assignment Command */ + NULL, /* Packet Assignment Command */ + + dtap_rr_cip_mode_cmd, /* Ciphering Mode Command */ + dtap_rr_cip_mode_cpte, /* Ciphering Mode Complete */ + + dtap_rr_conf_change_cmd, /* Configuration Change Command */ + NULL, /* Configuration Change Ack. */ + dtap_rr_conf_change_rej, /* Configuration Change Reject */ + + dtap_rr_ass_cmd, /* 9.1.2 Assignment Command */ + dtap_rr_ass_comp, /* Assignment Complete */ + dtap_rr_ass_fail, /* Assignment Failure */ + dtap_rr_ho_cmd, /* Handover Command */ + dtap_rr_ho_cpte, /* Handover Complete */ + dtap_rr_ho_fail, /* Handover Failure */ + dtap_rr_phy_info, /* Physical Information */ + NULL, /* DTM Assignment Command */ + + NULL, /* RR-cell Change Order */ + NULL, /* PDCH Assignment Command */ + + dtap_rr_ch_rel, /* Channel Release */ + dtap_rr_partial_rel, /* Partial Release */ + NULL, /* Partial Release Complete */ + + dtap_rr_paging_req_type_1, /* Paging Request Type 1 */ + dtap_rr_paging_req_type_2, /* Paging Request Type 2 */ + dtap_rr_paging_req_type_3, /* Paging Request Type 3 */ + dtap_rr_paging_resp, /* Paging Response */ + NULL, /* Notification/NCH */ + NULL, /* Reserved */ + NULL, /* Notification/Response */ + + NULL, /* Reserved */ + + dtap_rr_utran_classmark_change, /* Utran Classmark Change */ + NULL, /* cdma2000 Classmark Change */ + dtap_rr_inter_syst_to_utran_ho_cmd, /* Inter System to UTRAN Handover Command */ + NULL, /* Inter System to cdma2000 Handover Command */ + + NULL, /* System Information Type 8 */ + dtap_rr_sys_info_1, /* System Information Type 1 */ + dtap_rr_sys_info_2, /* System Information Type 2 */ + dtap_rr_sys_info_3, /* System Information Type 3 */ + dtap_rr_sys_info_4, /* System Information Type 4 */ + dtap_rr_sys_info_5, /* System Information Type 5 */ + dtap_rr_sys_info_6, /* System Information Type 6 */ + NULL, /* System Information Type 7 */ + + dtap_rr_sys_info_2bis, /* System Information Type 2bis */ + dtap_rr_sys_info_2ter, /* System Information Type 2ter */ + dtap_rr_sys_info_2quater, /* System Information Type 2quater */ + dtap_rr_sys_info_5bis, /* System Information Type 5bis */ + dtap_rr_sys_info_5ter, /* System Information Type 5ter */ + NULL, /* System Information Type 9 */ + dtap_rr_sys_info_13, /* System Information Type 13 */ + + NULL, /* System Information Type 16 */ + NULL, /* System Information Type 17 */ + + NULL, /* System Information Type 18 */ + NULL, /* System Information Type 19 */ + NULL, /* System Information Type 20 */ + + dtap_rr_ch_mode_mod, /* Channel Mode Modify */ + dtap_rr_rr_status, /* RR Status */ + dtap_rr_ch_mode_mod_ack, /* Channel Mode Modify Acknowledge */ + dtap_rr_freq_redef, /* Frequency Redefinition */ + dtap_rr_meas_rep, /* 9.1.21 Measurement report */ + dtap_rr_mm_cm_change, /* 9.1.11 Classmark Change */ + dtap_rr_cm_enq, /* Classmark Enquiry */ + dtap_rr_ext_meas_report, /* Extended Measurement Report */ + dtap_rr_ext_meas_order, /* Extended Measurement Order */ + dtap_rr_gprs_sus_req, /* 9.1.13b GPRS Suspension Request */ + + NULL, /* VGCS Uplink Grant */ + NULL, /* Uplink Release */ + NULL, /* Reserved */ + NULL, /* Uplink Busy */ + NULL, /* Talker Indication */ + + NULL, /* UTRAN Classmark Change/Handover To UTRAN Command */ /* spec conflict */ + + dtap_rr_app_inf, /* Application Information */ + + NULL, /* NONE */ +}; + +void get_rr_msg_params(guint8 oct, const gchar **msg_str, int *ett_tree, int *hf_idx, msg_fcn *msg_fcn_p) +{ + gint idx; + + *msg_str = match_strval_idx((guint32) (oct & DTAP_RR_IEI_MASK), gsm_a_dtap_msg_rr_strings, &idx); + *hf_idx = hf_gsm_a_dtap_msg_rr_type; + if (*msg_str != NULL) { + *ett_tree = ett_gsm_dtap_msg_rr[idx]; + *msg_fcn_p = dtap_msg_rr_fcn[idx]; + } + + return; +} +/* This is more or less a copy of the dissect_dtap() code just adding + * L2 Pseudo Length decoding first + * The code should probably be cleaned up. + * The name CCCH might not be correct! + */ +static void +dissect_ccch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + + static gsm_a_tap_rec_t tap_rec[4]; + static gsm_a_tap_rec_t *tap_p; + static guint tap_current=0; + + void (*msg_fcn_p)(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len); + guint8 oct; + guint8 pd; + guint32 offset, saved_offset; + guint32 len; + guint32 oct_1, oct_2; + proto_item *ccch_item = NULL; + proto_tree *ccch_tree = NULL; + proto_item *oct_1_item = NULL; + proto_tree *pd_tree = NULL; + proto_tree *saved_tree = NULL; + const gchar *msg_str; + gint ett_tree; + gint ti; + int hf_idx; + gboolean nsd; + /*guint8 pseudo_len;*/ + guint32 curr_offset; + guint32 consumed; + guint curr_len; + + len = tvb_length(tvb); + + if (len < 2){ + /* + * too short to be CCCH + */ + call_dissector(data_handle, tvb, pinfo, tree); + return; + } + + col_append_str(pinfo->cinfo, COL_INFO, "(CCCH) "); + /* + * set tap record pointer + */ + tap_current++; + if (tap_current >= 4) + { + tap_current = 0; + } + tap_p = &tap_rec[tap_current]; + + offset = 0; + oct_2 = 0; + + /* Skip pseeudo hdr here */ + offset = 1; + + /* + * get protocol discriminator + */ + oct_1 = tvb_get_guint8(tvb, offset++); + + if ((((oct_1 & DTAP_TI_MASK) >> 4) & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + /* + * eventhough we don't know if a TI should be in the message yet + * we rely on the TI/SKIP indicator to be 0 to avoid taking this + * octet + */ + oct_2 = tvb_get_guint8(tvb, offset++); + } + + oct = tvb_get_guint8(tvb, offset); + + pd = oct_1 & DTAP_PD_MASK; + ti = -1; + msg_str = NULL; + ett_tree = -1; + hf_idx = -1; + msg_fcn_p = NULL; + nsd = FALSE; + col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ",val_to_str(pd,gsm_a_pd_short_str_vals,"Unknown (%u)")); + + /* + * octet 1 + */ + switch (pd){ + case 6: + get_rr_msg_params(oct, &msg_str, &ett_tree, &hf_idx, &msg_fcn_p); + break; + + default: + /* XXX - hf_idx is still -1! this is a bug in the implementation, and I don't know how to fix it so simple return here */ + return; + } + + /* + * create the protocol tree + */ + if (msg_str == NULL){ + ccch_item = proto_tree_add_protocol_format(tree, proto_a_ccch, tvb, 0, len, + "GSM CCCH - Message Type (0x%02x)", + oct); + + ccch_tree = proto_item_add_subtree(ccch_item, ett_ccch_msg); + }else{ + ccch_item = proto_tree_add_protocol_format(tree, proto_a_ccch, tvb, 0, -1, + "GSM CCCH - %s", msg_str); + + ccch_tree = proto_item_add_subtree(ccch_item, ett_tree); + } + + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", msg_str); + + /* back to the begining */ + saved_offset = offset; + offset = 0; + + curr_offset = offset; + curr_len = len; + len = 1; + + /* L2 Pseudo Length 10.5.2.19 */ + /*pseudo_len = tvb_get_guint8(tvb,offset)>>2;*/ + + saved_tree = tree; + tree = ccch_tree; + ELEM_MAND_V(GSM_A_PDU_TYPE_RR, DE_RR_L2_PSEUDO_LEN, NULL); + tree = saved_tree; + offset = saved_offset; + len = curr_len; + + oct_1_item = + proto_tree_add_text(ccch_tree, + tvb, 1, 1, + "Protocol Discriminator: %s", + val_to_str(pd, protocol_discriminator_vals, "Unknown (%u)")); + + pd_tree = proto_item_add_subtree(oct_1_item, ett_ccch_oct_1); + proto_tree_add_item(pd_tree, hf_gsm_a_L3_protocol_discriminator, tvb, 1, 1, ENC_BIG_ENDIAN); + + if (ti == -1){ + proto_tree_add_item(pd_tree, hf_gsm_a_skip_ind, tvb, 1, 1, ENC_BIG_ENDIAN); + }else{ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x80, 8); + proto_tree_add_text(pd_tree,tvb, 1, 1, + "%s : TI flag: %s", + a_bigbuf, + ((oct_1 & 0x80) ? "allocated by receiver" : "allocated by sender")); + + if ((ti & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + /* ti is extended to next octet */ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x70, 8); + proto_tree_add_text(pd_tree, tvb, 1, 1, + "%s : TIO: The TI value is given by the TIE in octet 2", + a_bigbuf); + }else{ + other_decode_bitfield_value(a_bigbuf, oct_1, 0x70, 8); + proto_tree_add_text(pd_tree,tvb, 1, 1, + "%s : TIO: %u",a_bigbuf,ti & DTAP_TIE_PRES_MASK); + } + } + + + if ((ti != -1) && (ti & DTAP_TIE_PRES_MASK) == DTAP_TIE_PRES_MASK){ + proto_tree_add_item(tree, hf_gsm_a_extension, tvb, 2, 1, ENC_BIG_ENDIAN); + other_decode_bitfield_value(a_bigbuf, oct_2, DTAP_TIE_MASK, 8); + proto_tree_add_text(pd_tree, tvb, 2, 1, + "%s : TIE: %u", a_bigbuf, oct_2 & DTAP_TIE_MASK); + } + + /* + * N(SD) + */ + if ((pinfo->p2p_dir == P2P_DIR_RECV) && + nsd) + { + /* XXX */ + } + + /* + * add DTAP message name + */ + proto_tree_add_uint_format(ccch_tree, hf_idx, tvb, offset, 1, oct, + "Message Type: %s",msg_str ? msg_str : "(Unknown)"); + + offset++; + + tap_p->pdu_type = GSM_A_PDU_TYPE_DTAP; + tap_p->message_type = (nsd ? (oct & 0x3f) : oct); + tap_p->protocol_disc = pd; + + tap_queue_packet(gsm_a_tap, pinfo, tap_p); + + if (msg_str == NULL) + return; + + if (offset >= len) + return; + + /* + * decode elements + */ + if (msg_fcn_p == NULL){ + proto_tree_add_text(ccch_tree, tvb, offset, len - offset, + "Message Elements"); + }else{ + (*msg_fcn_p)(tvb, ccch_tree, pinfo, offset, len - offset); + } +} + +const value_string gsm_a_rr_short_pd_msg_strings[] = { + { 0x00, "System Information Type 10" }, + { 0x01, "Notification/FACCH" }, + { 0x02, "Uplink Free" }, + { 0x04, "Enhanced Measurement Report" }, + { 0x05, "Measurement Information" }, + { 0x06, "VBS/VGCS Reconfigure" }, + { 0x07, "VBS/VGCS Reconfigure2" }, + { 0x08, "VGCS Additional Information" }, + { 0x09, "VGCS SMS Information" }, + { 0, NULL } +}; + +#define NUM_GSM_SACCH_MSG_RR (sizeof(gsm_a_rr_short_pd_msg_strings)/sizeof(value_string)) +static gint ett_gsm_sacch_msg_rr[NUM_GSM_SACCH_MSG_RR]; +static void (*sacch_msg_rr_fcn[])(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset, guint len) = { + NULL, /* System Information Type 10 */ + NULL, /* Notification/FACCH */ + NULL, /* Uplink Free */ + sacch_rr_enh_meas_report, /* Enhanced Measurement Report */ + sacch_rr_meas_info, /* Measurement Information */ + NULL, /* VBS/VGCS Reconfigure */ + NULL, /* VBS/VGCS Reconfigure2 */ + NULL, /* VGCS Additional Information */ + NULL, /* VGCS SMS Information */ + NULL, /* NONE */ +}; + +static void +get_rr_short_pd_msg_params(guint8 mess_type, const gchar **msg_str, int *ett_tree, int *hf_idx, msg_fcn *msg_fcn_p) +{ + gint idx; + + *msg_str = match_strval_idx((guint32) mess_type, gsm_a_rr_short_pd_msg_strings, &idx); + *hf_idx = hf_gsm_a_rr_short_pd_msg_type; + if (*msg_str != NULL) { + *ett_tree = ett_gsm_sacch_msg_rr[idx]; + *msg_fcn_p = sacch_msg_rr_fcn[idx]; + } +} + +const value_string short_protocol_discriminator_vals[] = { + {0x0, "Radio Resources Management messages"}, + { 0, NULL } +}; + +static void +dissect_sacch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + static gsm_a_tap_rec_t tap_rec[4]; + static gsm_a_tap_rec_t *tap_p; + static guint tap_current=0; + + void (*msg_fcn_p)(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo _U_, guint32 offset, guint len); + guint8 oct, short_pd, mess_type; + guint32 offset, bit_offset = 0; + guint32 len; + proto_item *sacch_item = NULL; + proto_tree *sacch_tree = NULL; + const gchar *msg_str; + gint ett_tree; + int hf_idx; + + len = tvb_length(tvb); + + col_append_str(pinfo->cinfo, COL_INFO, "(SACCH) "); + + /* + * set tap record pointer + */ + tap_current++; + if (tap_current >= 4) + { + tap_current = 0; + } + tap_p = &tap_rec[tap_current]; + + offset = 0; + + oct = tvb_get_guint8(tvb, offset); + + msg_str = NULL; + ett_tree = -1; + hf_idx = -1; + msg_fcn_p = NULL; + + short_pd = (oct & 0x80) >> 7; + mess_type = (oct & 0x7c) >> 2; + + if (short_pd == 0) + { + col_append_fstr(pinfo->cinfo, COL_INFO, "(RR) "); + get_rr_short_pd_msg_params(mess_type, &msg_str, &ett_tree, &hf_idx, &msg_fcn_p); + } + else + { + col_append_fstr(pinfo->cinfo, COL_INFO, "(Unknown) "); + } + + /* + * create the protocol tree + */ + if (msg_str == NULL){ + sacch_item = proto_tree_add_protocol_format(tree, proto_a_sacch, tvb, 0, len, + "GSM SACCH - Message Type (0x%02x)", mess_type); + + sacch_tree = proto_item_add_subtree(sacch_item, ett_sacch_msg); + }else{ + sacch_item = proto_tree_add_protocol_format(tree, proto_a_sacch, tvb, 0, -1, + "GSM SACCH - %s", msg_str); + + sacch_tree = proto_item_add_subtree(sacch_item, ett_tree); + + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", msg_str); + } + + if (short_pd == 0) + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_pd, tvb, offset * 8 + bit_offset++, 1, ENC_BIG_ENDIAN); + + if (hf_idx == -1) + return; + + /* + * add SACCH message name + */ + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_pd_msg_type, tvb, offset * 8 + bit_offset, 5, ENC_BIG_ENDIAN); + bit_offset += 5; + + proto_tree_add_bits_item(sacch_tree, hf_gsm_a_rr_short_l2_header, tvb, offset * 8 + bit_offset, 2, ENC_BIG_ENDIAN); + offset++; + + tap_p->pdu_type = GSM_A_PDU_TYPE_SACCH; + tap_p->message_type = mess_type; + tap_p->protocol_disc = short_pd; + + tap_queue_packet(gsm_a_tap, pinfo, tap_p); + + if (msg_str == NULL) + return; + + /* + * decode elements + */ + if (msg_fcn_p == NULL){ + proto_tree_add_text(sacch_tree, tvb, offset, len - offset, + "Message Elements"); + }else{ + (*msg_fcn_p)(tvb, sacch_tree, pinfo, offset, len - offset); + } +} + +/* Register the protocol with Wireshark */ +void +proto_register_gsm_a_rr(void) +{ + guint i; + guint last_offset; + + /* Setup list of header fields */ + + static hf_register_info hf[] = + { + { &hf_gsm_a_dtap_msg_rr_type, + { "DTAP Radio Resources Management Message Type", "gsm_a.dtap_msg_rr_type", + FT_UINT8, BASE_HEX, VALS(gsm_a_dtap_msg_rr_strings), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_elem_id, + { "Element ID", "gsm_a_rr.elem_id", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_gsm_a_bcc, + { "BCC","gsm_a.bcc", + FT_UINT8,BASE_DEC, NULL, 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_ncc, + { "NCC","gsm_a.ncc", + FT_UINT8,BASE_DEC, NULL, 0x38, + NULL, HFILL } + }, + { &hf_gsm_a_bcch_arfcn, + { "BCCH ARFCN(RF channel number)","gsm_a.bcch_arfcn", + FT_UINT16,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_range_nb, + { "Number of Ranges","gsm_a.rr.range_nb", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_range_lower, + { "Range Lower","gsm_a.rr.range_lower", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN used as the lower limit of a range of frequencies to be used by the mobile station in cell selection (Range Lower)", HFILL } + }, + { &hf_gsm_a_rr_range_higher, + { "Range Higher","gsm_a.rr.range_higher", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN used as the higher limit of a range of frequencies to be used by the mobile station in cell selection (Range Higher)", HFILL } + }, + { &hf_gsm_a_rr_ba_list_pref_length, + { "Length of BA List Pref","gsm_a.rr.ba_list_pref_length", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ba_freq, + { "BA Freq","gsm_a.rr.ba_freq", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "ARFCN indicating a single frequency to be used by the mobile station in cell selection and reselection (BA Freq)", HFILL } + }, + { &hf_gsm_a_rr_utran_freq_list_length, + { "Length of UTRAN freq list","gsm_a.rr.utran_freq_length", + FT_UINT8, BASE_DEC, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ho_ref_val, + { "Handover reference value","gsm_a.rr.ho_ref_val", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_L2_pseudo_len, + { "L2 Pseudo Length value","gsm_a.rr.rr_2_pseudo_len", + FT_UINT8, BASE_DEC, NULL, 0xfc, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ba_used, + { "BA-USED","gsm_a.rr.ba_used", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dtx_used, + { "DTX-USED","gsm_a.rr.dtx_used", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtx_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_ba_used, + { "3G-BA-USED","gsm_a.rr.3g_ba_used", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_meas_valid, + { "MEAS-VALID","gsm_a.rr.meas_valid", + FT_BOOLEAN,8, TFS(&gsm_a_rr_mv_vals), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_full_serv_cell, + { "RXLEV-FULL-SERVING-CELL","gsm_a.rr.rxlev_full_serv_cell", + FT_UINT8,BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_sub_serv_cell, + { "RXLEV-SUB-SERVING-CELL","gsm_a.rr.rxlev_sub_serv_cell", + FT_UINT8,BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxqual_full_serv_cell, + { "RXQUAL-FULL-SERVING-CELL","gsm_a.rr.rxqual_full_serv_cell", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_rxqual_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxqual_sub_serv_cell, + { "RXQUAL-SUB-SERVING-CELL","gsm_a.rr.rxqual_sub_serv_cell", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_rxqual_vals), 0x0e, + NULL, HFILL } + }, + { &hf_gsm_a_rr_no_ncell_m, + { "NO-NCELL-M","gsm_a.rr.no_ncell_m", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_ncell_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rxlev_ncell, + { "RXLEV-NCELL","gsm_a.rr.rxlev_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bcch_freq_ncell, + { "BCCH-FREQ-NCELL","gsm_a.rr.bcch_freq_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bsic_ncell, + { "BSIC-NCELL","gsm_a.rr.bsic_ncell", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mobile_time_difference, + { "Mobile Timing Difference value (in half bit periods)","gsm_a.rr.mobile_time_difference", + FT_UINT32,BASE_DEC, NULL, 0xFFFFF8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_atc, + { "ATC","gsm_a.rr.pow_cmd_atc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_atc_value), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_page_mode, + { "Page Mode","gsm_a.rr.page_mode", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_page_mode_vals), 0x0F, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dedicated_mode_or_tbf, + { "Dedicated mode or TBF","gsm_a.rr.dedicated_mode_or_tbf", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_dedicated_mode_or_tbf_vals), 0xF0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_epc, + { "EPC_mode","gsm_a.rr.pow_cmd_epc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_epc_value), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_fpcepc, + { "FPC_EPC","gsm_a.rr.pow_cmd_fpcepc", + FT_BOOLEAN,8, TFS(&gsm_a_rr_pow_cmd_fpcepc_value), 0x20, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pow_cmd_powlev, + { "POWER LEVEL","gsm_a.rr.pow_cmd_pow", + FT_UINT8,BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_nci, + { "Normal cell indication(NCI)","gsm_a.rr.sync_ind_nci", + FT_BOOLEAN,8, TFS(&gsm_a_rr_sync_ind_nci_value), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_rot, + { "Report Observed Time Difference(ROT)","gsm_a.rr.sync_ind_rot", + FT_BOOLEAN,8, TFS(&sm_a_rr_sync_ind_rot_value), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sync_ind_si, + { "Synchronization indication(SI)","gsm_a.rr_sync_ind_si", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_sync_ind_si_vals), 0x03, + NULL, HFILL } + }, + { &hf_gsm_a_rr_format_id, + { "Format Identifier","gsm_a.rr_format_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_freq_list_format_id_vals), 0xce, + NULL, HFILL } + }, + { &hf_gsm_a_rr_format_id2, + { "Format Identifier","gsm_a.rr_format_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_freq_list_format_id_vals), 0x8e, + NULL, HFILL } + }, + { &hf_gsm_a_rr_channel_mode, + { "Channel Mode","gsm_a.rr.channel_mode", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_mode_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_channel_mode2, + { "Channel Mode 2","gsm_a.rr.channel_mode2", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_mode2_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_sc, + { "SC","gsm_a.rr.SC", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_sc_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_algorithm_id, + { "Algorithm identifier","gsm_a.algorithm_identifier", + FT_UINT8,BASE_DEC, VALS(gsm_a_algorithm_identifier_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cr, + { "CR","gsm_a.rr.CR", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_cr_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_multirate_speech_ver, + { "Multirate speech version","gsm_a.rr.multirate_speech_ver", + FT_UINT8,BASE_DEC, VALS(multirate_speech_ver_vals), 0xe0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_NCSB, + { "NSCB: Noise Suppression Control Bit","gsm_a.rr.NCSB", + FT_UINT8,BASE_DEC, VALS(NSCB_vals), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ICMI, + { "ICMI: Initial Codec Mode Indicator","gsm_a.rr.ICMI", + FT_UINT8,BASE_DEC, VALS(ICMI_vals), 0x8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_start_mode, + { "Start Mode","gsm_a.rr.start_mode", + FT_UINT8,BASE_DEC, NULL, 0x3, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv, + { "Timing advance value","gsm_a.rr.timing_adv", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_time_diff, + { "Time difference value","gsm_a.rr.time_diff", + FT_UINT8,BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tlli, + { "TLLI","gsm_a.rr.tlli", + FT_UINT32,BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tmsi_ptmsi, + { "TMSI/P-TMSI Value","gsm_a.rr.tmsi_ptmsi", + FT_UINT32,BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_target_mode, + { "Target mode","gsm_a.rr.target_mode", + FT_UINT8,BASE_DEC, NULL, 0xc0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_wait_indication, + { "Wait Indication","gsm_a.rr.wait_indication", + FT_UINT8,BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_seq_code, + { "Sequence Code","gsm_a.rr.seq_code", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + + { &hf_gsm_a_rr_group_cipher_key_number, + { "Group cipher key number","gsm_a.rr.Group_cipher_key_number", + FT_UINT8,BASE_DEC, NULL, 0x3c, + NULL, HFILL } + }, + { &hf_gsm_a_rr_MBMS_broadcast, + { "MBMS Broadcast","gsm_a.rr.MBMS_broadcast", + FT_BOOLEAN,8, TFS(&gsm_a_rr_MBMS_broadcast_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_MBMS_multicast, + { "MBMS Multicast","gsm_a.rr.MBMS_multicast", + FT_BOOLEAN,8, TFS(&gsm_a_rr_MBMS_multicast_value), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_last_segment, + { "Last Segment","gsm_a.rr.last_segment", + FT_BOOLEAN,8, TFS(&gsm_a_rr_last_segment_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_carrier_ind, + { "Carrier Indication","gsm_a.rr.carrier_ind", + FT_BOOLEAN,8, TFS(&gsm_a_rr_carrier_ind_value), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ra, + { "Random Access Information (RA)", "gsm_a_rr_ra", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T1prim, + { "T1'", "gsm_a.rr.T1prim", + FT_UINT8, BASE_DEC, NULL, 0xf8, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T3, + { "T3", "gsm_a.rr.T3", + FT_UINT16, BASE_DEC, NULL, 0x07e0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_T2, + { "T2", "gsm_a.rr.T2", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rfn, + { "RFN", "gsm_a.rr.rfn", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Reduced Frame Number", HFILL } + }, + { &hf_gsm_a_rr_RR_cause, + { "RR cause value","gsm_a.rr.RRcause", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_RR_cause_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cm_cng_msg_req, + { "CLASSMARK CHANGE","gsm_a.rr_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_cm_cng_msg_req, + { "UTRAN CLASSMARK CHANGE","gsm_a.rr_utran_cm_cng_msg_req", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_utran_cm_cng_msg_req_vals), 0x70, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cdma200_cm_cng_msg_req, + { "CDMA2000 CLASSMARK CHANGE","gsm_a.rr_cdma200_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_geran_iu_cm_cng_msg_req, + { "GERAN IU MODE CLASSMARK CHANGE","gsm_a.rr_geran_iu_cm_cng_msg_req", + FT_BOOLEAN,8, TFS(&gsm_a_msg_req_value), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch1, + { "Channel 1","gsm_a.rr_chnl_needed_ch1", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch2, + { "Channel 2","gsm_a.rr_chnl_needed_ch2", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch3, + { "Channel 3","gsm_a.rr_chnl_needed_ch3", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_chnl_needed_ch4, + { "Channel 4","gsm_a.rr_chnl_needed_ch4", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_channel_needed_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_suspension_cause, + { "Suspension cause value","gsm_a.rr.suspension_cause", + FT_UINT8,BASE_DEC, VALS(gsm_a_rr_suspension_cause_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_apdu_id, + { "APDU ID","gsm_a.rr.apdu_id", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_id_vals), 0x0f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_apdu_flags, + { "APDU Flags","gsm_a.rr.apdu_flags", + FT_UINT8,BASE_HEX, VALS(gsm_a_rr_apdu_flags_vals), 0xf0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b8, + { "12,2 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b8", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x80, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b7, + { "10,2 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b7", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x40, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b6, + { "7,95 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b6", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x20, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b5, + { "7,40 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b5", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b4, + { "6,70 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b4", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b3, + { "5,90 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b3", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b2, + { "5,15 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b2", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v1_b1, + { "4,75 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v1b1", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b5, + { "23,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b5", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x10, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b4, + { "15,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b4", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x08, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b3, + { "12,65 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b3", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x04, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b2, + { "8,85 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b2", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x02, + NULL, HFILL } + }, + { &hf_gsm_a_rr_set_of_amr_codec_modes_v2_b1, + { "6,60 kbit/s codec rate", "gsm_a.rr.set_of_amr_codec_modes_v2b1", + FT_BOOLEAN,8, TFS(&gsm_a_rr_set_of_amr_codec_modes), 0x01, + NULL, HFILL } + }, + { &hf_gsm_a_rr_amr_threshold, + { "AMR Threshold", "gsm_a.rr.amr_threshold", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_amr_threshold_vals_ext, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_amr_hysteresis, + { "AMR Hysteresis", "gsm_a.rr.amr_hysteresis", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_amr_hysteresis_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pwrc, + { "PWRC", "gsm_a.rr.pwrc", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Power Control Indicator (PWRC)", HFILL } + }, + { &hf_gsm_a_rr_dtx_bcch, + { "DTX (BCCH)", "gsm_a.rr.dtx_bcch", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_dtx_bcch_vals), 0x30, + "Discontinuous Transmission (DTX-BCCH)", HFILL } + }, + { &hf_gsm_a_rr_dtx_sacch, + { "DTX (SACCH)", "gsm_a.rr.dtx_sacch", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_dtx_sacch_vals), 0xb0, + "Discontinuous Transmission (DTX-SACCH)", HFILL } + }, + { &hf_gsm_a_rr_radio_link_timeout, + { "Radio Link Timeout", "gsm_a.rr.radio_link_timeout", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_radio_link_timeout_vals), 0x0f, + "Radio Link Timeout (s)", HFILL } + }, + { &hf_gsm_a_rr_cell_reselect_hyst, + { "Cell Reselection Hysteresis", "gsm_a.rr.cell_reselect_hyst", + FT_UINT8, BASE_DEC, NULL, 0xe0, + "Cell Reselection Hysteresis (dB)", HFILL } + }, + { &hf_gsm_a_rr_ms_txpwr_max_cch, + { "MS TXPWR MAX CCH", "gsm_a.rr.ms_txpwr_max_cch", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_acs, + { "ACS", "gsm_a.rr.acs", + FT_BOOLEAN, 8, NULL, 0x80, + "Additional Reselect Param Indicator (ACS)", HFILL } + }, + { &hf_gsm_a_rr_neci, + { "NECI", "gsm_a.rr.neci", + FT_UINT8, BASE_DEC, NULL, 0x40, + "New Establishment Cause Indicator (NECI)", HFILL } + }, + { &hf_gsm_a_rr_rxlev_access_min, + { "RXLEV-ACCESS-MIN", "gsm_a.rr.rxlev_access_min", + FT_UINT8, BASE_DEC|BASE_EXT_STRING, &gsm_a_rr_rxlev_vals_ext, 0x3f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mscr, + { "MSCR", "gsm_a.rr.mscr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_mscr_vals), 0x80, + "MSC Release Indicator (MSCR)", HFILL } + }, + { &hf_gsm_a_rr_att, + { "ATT", "gsm_a.rr.att", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_att_vals), 0x40, + "Attach Indicator (ATT)", HFILL } + }, + { &hf_gsm_a_rr_bs_ag_blks_res, + { "BS_AG_BLKS_RES", "gsm_a.rr.bs_ag_blks_res", + FT_UINT8, BASE_DEC, NULL, 0x38, + "Access Grant Reserved Blocks (BS_AG_BLKS_RES)", HFILL } + }, + { &hf_gsm_a_rr_ccch_conf, + { "CCCH-CONF", "gsm_a.rr.ccch_conf", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_ccch_conf_vals), 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_rr_cbq3, + { "CBQ3", "gsm_a.rr.cbq3", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cbq3_vals), 0x00, + "Cell Bar Qualify 3", HFILL } + }, + { &hf_gsm_a_rr_bs_pa_mfrms, + { "BS-PA-MFRMS", "gsm_a.rr.bs_pa_mfrms", + FT_UINT8, BASE_DEC, NULL, 0x07, + NULL, HFILL } + }, + { &hf_gsm_a_rr_t3212, + { "T3212", "gsm_a.rr.t3212", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Periodic Update period (T3212) (deci-hours)", HFILL } + }, + { &hf_gsm_a_rr_dyn_arfcn_length, + { "Length of Dynamic Mapping", "gsm_a.rr.dyn_arfcn_length", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gsm_band, + { "GSM Band", "gsm_a.rr.gsm_band", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_band_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn_first, + { "ARFCN First", "gsm_a.rr.arfcn_first", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_band_offset, + { "Band Offset", "gsm_a.rr.band_offset", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn_range, + { "ARFCN Range", "gsm_a.rr.arfcn_range", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_lowest_arfcn, + { "Lowest ARFCN", "gsm_a.rr.lowest_arfcn", + FT_UINT8, BASE_DEC, NULL, 0x7f, + NULL, HFILL } + }, + { &hf_gsm_a_rr_inc_skip_arfcn, + { "Increment skip ARFCN", "gsm_a.rr.inc_skip_arfcn", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gprs_resumption_ack, + { "Ack", "gsm_a.rr.gprs_resumption_ack", + FT_BOOLEAN, 8, TFS(&gsm_a_rr_gprs_resumption_ack_value), 0x01, + "GPRS Resumption Ack bit", HFILL } + }, + { &hf_gsm_a_rr_ext_ind, + { "EXT-IND", "gsm_a.rr.ext_ind", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_ext_ind_vals), 0x20, + "Extension Indication (EXT-IND)", HFILL } + }, + { &hf_gsm_a_rr_ba_ind, + { "BA-IND", "gsm_a.rr.ba_ind", + FT_UINT8, BASE_DEC, NULL, 0x00, + "BCCH Allocation Indication (BA-IND)", HFILL } + }, + { &hf_gsm_a_rr_multiband_reporting, + { "Multiband Reporting", "gsm_a.rr.multiband_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells to be reported in each band if Multiband Reporting", HFILL } + }, + { &hf_gsm_a_rr_ncc_permitted, + { "NCC Permitted", "gsm_a.rr.ncc_permitted", + FT_UINT8, BASE_HEX, NULL, 0xff, + NULL, HFILL } + }, + { &hf_gsm_a_rr_max_retrans, + { "Max retrans", "gsm_a.rr.max_retrans", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_max_retrans_vals), 0xc0, + "Maximum number of retransmissions", HFILL } + }, + { &hf_gsm_a_rr_tx_integer, + { "Tx-integer", "gsm_a.rr.tx_integer", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_tx_integer_vals), 0x3c, + "Number of Slots to spread Transmission (Tx-integer)", HFILL } + }, + { &hf_gsm_a_rr_cell_barr_access, + { "CELL_BARR_ACCESS", "gsm_a.rr.cell_barr_access", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cell_barr_access_vals), 0x02, + "Cell Barred for Access (CELL_BARR_ACCESS)", HFILL } + }, + { &hf_gsm_a_rr_re, + { "RE", "gsm_a.rr.re", + FT_BOOLEAN, 8, NULL, 0x01, + "Call re-establishment allowed (RE)", HFILL } + }, + { &hf_gsm_a_rr_acc, + { "ACC", "gsm_a.rr.acc", + FT_UINT16, BASE_HEX, NULL, 0xffff, + "Access Control Class N barred (ACC)", HFILL } + }, + { &hf_gsm_a_rr_nch_position, + { "NCH Position", "gsm_a.rr.nch_position", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nch_position_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_mp_change_mark, + { "SI2ter Measurement Parameter Change Mark", "gsm_a.rr.si2ter_mp_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_3g_change_mark, + { "SI2ter 3G Change Mark", "gsm_a.rr.si2ter_3g_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_index, + { "SI2ter Index", "gsm_a.rr.si2ter_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2ter_count, + { "SI2ter Count", "gsm_a.rr.si2ter_count", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_fdd_uarfcn, + { "FDD UARFCN", "gsm_a.rr.fdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bandwidth_fdd, + { "Bandwidth FDD", "gsm_a.rr.bandwidth_fdd", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tdd_uarfcn, + { "TDD UARFCN", "gsm_a.rr.tdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bandwidth_tdd, + { "Bandwidth TDD", "gsm_a.rr.bandwidth_tdd", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_arfcn, + { "ARFCN", "gsm_a.rr.arfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + "Absolute Radio Frequency Channel Number (ARFCN)", HFILL } + }, + { &hf_gsm_a_rr_bsic, + { "BSIC", "gsm_a.rr.bsic", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Base Station Identify Code (BSIC)", HFILL } + }, + { &hf_gsm_a_rr_qsearch_i, + { "Qsearch I", "gsm_a.rr.qsearch_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level is below (0 7) or above (8 15) threshold (Qsearch I)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qoffset, + { "FDD Qoffset", "gsm_a.rr.fdd_qoffset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xdd_qoffset_vals), 0x00, + "Offset to RLA_C for cell re selection to FDD access technology (FDD Qoffset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qmin, + { "FDD Qmin", "gsm_a.rr.fdd_qmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_qmin_vals), 0x00, + "Minimum threshold for Ec/No for UTRAN FDD cell re-selection (FDD Qmin)", HFILL } + }, + { &hf_gsm_a_rr_tdd_qoffset, + { "TDD Qoffset", "gsm_a.rr.tdd_qoffset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xdd_qoffset_vals), 0x00, + "Offset to RLA_C for cell re selection to TDD access technology (TDD Qoffset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_qmin_offset, + { "FDD Qmin Offset", "gsm_a.rr.fdd_qmin_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_qmin_offset_vals), 0x00, + "Offset to FDD Qmin value (FDD Qmin Offset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_rscpmin, + { "FDD RSCPmin", "gsm_a.rr.fdd_rscpmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_fdd_rscpmin_vals), 0x00, + "Minimum threshold of RSCP for UTRAN FDD cell re-selection (FDD RSCPmin)", HFILL } + }, + { &hf_gsm_a_rr_3g_ba_ind, + { "3G BA-IND", "gsm_a.rr.3g_ba_ind", + FT_UINT8, BASE_DEC, NULL, 0x00, + "3G BCCH Allocation Indication (3G BA-IND)", HFILL } + }, + { &hf_gsm_a_rr_mp_change_mark, + { "Measurement Parameter Change Mark", "gsm_a.rr.mp_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2quater_index, + { "SI2quater Index", "gsm_a.rr.si2quater_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si2quater_count, + { "SI2quater Count", "gsm_a.rr.si2quater_count", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gsm_report_type, + { "Report Type", "gsm_a.rr.gsm_report_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_gsm_report_type_value), 0x0, + "Report type the MS shall use (Report Type)", HFILL } + }, + { &hf_gsm_a_rr_serving_band_reporting, + { "Serving Band Reporting", "gsm_a.rr.serving_band_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells reported from the GSM serving frequency band (Serving Band Reporting)", HFILL } + }, + { &hf_gsm_a_rr_frequency_scrolling, + { "Frequency Scrolling", "gsm_a.rr.frequency_scrolling", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_frequency_scrolling_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rep_priority, + { "Rep Priority", "gsm_a.rr.rep_priority", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_rep_priority_value), 0x0, + "Reporting Priority", HFILL } + }, + { &hf_gsm_a_rr_report_type, + { "Report Type", "gsm_a.rr.report_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_report_type_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reporting_rate, + { "Reporting Rate", "gsm_a.rr.reporting_rate", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_reporting_rate_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_invalid_bsic_reporting, + { "Invalid BSIC Reporting", "gsm_a.rr.invalid_bsic_reporting", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_invalid_bsic_reporting_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_scale_ord, + { "Scale Ord", "gsm_a.rr.scale_ord", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_scale_ord_vals), 0x00, + "Offset used for the reported RXLEV values (Scale Ord)", HFILL } + }, + { &hf_gsm_a_rr_900_reporting_offset, + { "900 Reporting Offset", "gsm_a.rr.900_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 900 (900 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_900_reporting_threshold, + { "900 Reporting Threshold", "gsm_a.rr.900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 900 (900 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_1800_reporting_offset, + { "1800 Reporting Offset", "gsm_a.rr.1800_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 1800 (1800 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_1800_reporting_threshold, + { "1800 Reporting Threshold", "gsm_a.rr.1800_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 1800 (1800 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_400_reporting_offset, + { "400 Reporting Offset", "gsm_a.rr.400_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 400 (400 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_400_reporting_threshold, + { "400 Reporting Threshold", "gsm_a.rr.400_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 400 (400 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_1900_reporting_offset, + { "1900 Reporting Offset", "gsm_a.rr.1900_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 1900 (1900 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_1900_reporting_threshold, + { "1900 Reporting Threshold", "gsm_a.rr.1900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 1900 (1900 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_850_reporting_offset, + { "850 Reporting Offset", "gsm_a.rr.850_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 850 (850 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_850_reporting_threshold, + { "850 Reporting Threshold", "gsm_a.rr.900_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 850 (850 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_network_control_order, + { "Network Control Order", "gsm_a.rr.network_control_order", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_network_control_order_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nc_non_drx_period, + { "NC Non DRX Period", "gsm_a.rr.nc_non_drx_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_non_drx_period_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nc_reporting_period_i, + { "NC Reporting Period I", "gsm_a.rr.nc_reporting_period_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_reporting_period_x_vals), 0x00, + "NC Reporting Period in Packet Idle mode (NC Reporting Period I)", HFILL } + }, + { &hf_gsm_a_rr_nc_reporting_period_t, + { "NC Reporting Period T", "gsm_a.rr.nc_reporting_period_t", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nc_reporting_period_x_vals), 0x00, + "NC Reporting Period in Packet Transfer mode (NC Reporting Period T)", HFILL } + }, + { &hf_gsm_a_rr_index_start_3g, + { "Index Start 3G", "gsm_a.rr.index_start_3g", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_absolute_index_start_emr, + { "Absolute Index Start EMR", "gsm_a.rr.absolute_index_start_emr", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c_initial, + { "QSearch C Initial", "gsm_a.rr.qsearch_c_initial", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_qsearch_c_initial_value), 0x0, + "Qsearch value to be used in connected mode before Qsearch C is received (QSearch C Initial)", HFILL } + }, + { &hf_gsm_a_rr_fdd_rep_quant, + { "FDD Rep Quant", "gsm_a.rr.fdd_rep_quant", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_fdd_rep_quant_value), 0x0, + "FDD Reporting Quantity (FDD Rep Quant)", HFILL } + }, + { &hf_gsm_a_rr_fdd_multirat_reporting, + { "FDD Multirat Reporting", "gsm_a.rr.fdd_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells from the FDD access technology that shall be included in the list of strongest cells or in the measurement report (FDD Multirat Reporting)", HFILL } + }, + { &hf_gsm_a_rr_tdd_multirat_reporting, + { "TDD Multirat Reporting", "gsm_a.rr.tdd_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of cells from the TDD access technology that shall be included in the list of strongest cells or in the measurement report (TDD Multirat Reporting)", HFILL } + }, + { &hf_gsm_a_rr_qsearch_p, + { "Qsearch P", "gsm_a.rr.qsearch_p", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level below threshold (Qsearch P)", HFILL } + }, + { &hf_gsm_a_rr_3g_search_prio, + { "3G Search Prio (ignored in Rel-8)", "gsm_a.rr.3g_search_prio", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_3g_search_prio_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_offset, + { "FDD Reporting Offset", "gsm_a.rr.fdd_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for FDD access technology (FDD Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_rscp, + { "FDD Reporting Threshold RSCP", "gsm_a.rr.fdd_reporting_threshold_rscp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_rscp_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for FDD access technology (FDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_ecn0, + { "FDD Reporting Threshold EcN0", "gsm_a.rr.fdd_reporting_threshold_ecn0", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_ecn0_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for FDD access technology (FDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_offset, + { "TDD Reporting Offset", "gsm_a.rr.tdd_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for TDD access technology (TDD Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_threshold_rscp, + { "TDD Reporting Threshold RSCP", "gsm_a.rr.tdd_reporting_threshold_rscp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_rscp_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for TDD access technology (TDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_tdd_reporting_threshold_ecn0, + { "TDD Reporting Threshold EcN0", "gsm_a.rr.tdd_reporting_threshold_ecn0", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_wcdma_ecn0_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for TDD access technology (TDD Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_fdd_reporting_threshold_2, + { "FDD Reporting Threshold 2", "gsm_a.rr.fdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Reporting threshold for the CPICH parameter (Ec/No or RSCP) that is not reported according to FDD_REP_QUANT (FDD Reporting Threshold 2)", HFILL } + }, + { &hf_gsm_a_rr_3g_ccn_active, + { "3G CCN Active", "gsm_a.rr.3g_ccn_active", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_3g_ccn_active_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_700_reporting_offset, + { "700 Reporting Offset", "gsm_a.rr.700_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 700 (700 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_700_reporting_threshold, + { "700 Reporting Threshold", "gsm_a.rr.700_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 700 (700 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_810_reporting_offset, + { "810 Reporting Offset", "gsm_a.rr.810_reporting_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_xxx_reporting_offset_vals), 0x00, + "Offset to the reported value when prioritising the cells for reporting for GSM frequency band 810 (810 Reporting Offset)", HFILL } + }, + { &hf_gsm_a_rr_810_reporting_threshold, + { "810 Reporting Threshold", "gsm_a.rr.810_reporting_threshold", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gsm_reporting_threshold_vals), 0x00, + "Apply priority reporting if the reported value is above threshold for GSM frequency band 810 (810 Reporting Threshold)", HFILL } + }, + { &hf_gsm_a_rr_cbq, + { "CBQ", "gsm_a.rr.cbq", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Cell Bar Qualify", HFILL } + }, + { &hf_gsm_a_rr_cell_reselect_offset, + { "Cell Reselect Offset", "gsm_a.rr.cell_reselect_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cell_reselect_offset_vals), 0x00, + "Offset to the C2 reselection criterion (Cell Reselect Offset)", HFILL } + }, + { &hf_gsm_a_rr_temporary_offset, + { "Temporary Offset", "gsm_a.rr.temporary_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_temporary_offset_vals), 0x0, + "Negative offset to C2 for the duration of Penalty Time (Temporary Offset)", HFILL } + }, + { &hf_gsm_a_rr_penalty_time, + { "Penalty Time", "gsm_a.rr.penalty_time", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_penalty_time_vals), 0x0, + "Duration for which the temporary offset is applied (Penalty Time)", HFILL } + }, + { &hf_gsm_a_rr_gprs_ra_colour, + { "GPRS RA Colour", "gsm_a.rr.gprs_ra_colour", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13_position, + { "SI13 Position", "gsm_a.rr.si13_position", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si13_position_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_power_offset, + { "Power Offset", "gsm_a.rr.power_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_power_offset_vals), 0x0, + "Power offset used in conjunction with the MS TXPWR MAX CCH parameter by the class 3 DCS 1800 MS (Power Offset)", HFILL } + }, + { &hf_gsm_a_rr_si2quater_position, + { "SI2quater Position", "gsm_a.rr.si2quater_position", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si2quater_position_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13alt_position, + { "SI13alt Position", "gsm_a.rr.si13alt_position", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si13alt_position_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_prio_thr, + { "Prio Thr", "gsm_a.rr.prio_thr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_prio_thr_vals), 0x0, + "Prio signal strength threshold is related to RXLEV ACCESS_MIN (Prio Thr)", HFILL } + }, + { &hf_gsm_a_rr_lsa_offset, + { "LSA Offset", "gsm_a.rr.lsa_offset", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lsa_offset_vals), 0x0, + "Offset to be used for LSA cell re selection between cells with the same LSA priorities (LSA Offset)", HFILL } + }, + { &hf_gsm_a_rr_paging_channel_restructuring, + { "Paging Channel Restructuring", "gsm_a.rr.paging_channel_restructuring", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_paging_channel_restructuring_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_sacch, + { "NLN (SACCH)", "gsm_a.rr.nln_sacch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_status_sacch, + { "NLN Status (SACCH)", "gsm_a.rr.nln_status_sacch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_pch, + { "NLN (PCH)", "gsm_a.rr.nln_pch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nln_status_pch, + { "NLN Status (PCH)", "gsm_a.rr.nln_status_pch", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_vbs_vgcs_inband_notifications, + { "Inband Notifications", "gsm_a.rr.vbs_vgcs_inband_notifications", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_vbs_vgcs_inband_notifications_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_vbs_vgcs_inband_pagings, + { "Inband Pagings", "gsm_a.rr.vbs_vgcs_inband_pagings", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_vbs_vgcs_inband_pagings_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rac, + { "RAC", "gsm_a.rr.rac", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Routeing Area Code", HFILL } + }, + { &hf_gsm_a_rr_max_lapdm, + { "Max LAPDm", "gsm_a.rr.max_lapdm", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_max_lapdm_vals), 0x0, + "Maximum number of LAPDm frames on which a layer 3 can be segmented into and be sent on the main DCCH (Max LAPDm)", HFILL } + }, + { &hf_gsm_a_rr_gprs_ms_txpwr_max_ccch, + { "GPRS MS TxPwr Max CCH", "gsm_a.rr.gprs_ms_txpwr_max_cch", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dedicated_mode_mbms_notification_support, + { "Dedicated Mode MBMS Notification Support", "gsm_a.rr.dedicated_mode_mbms_notification_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dedicated_mode_mbms_notification_support_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mnci_support, + { "MNCI Support", "gsm_a.rr.mnci_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_mnci_support_value), 0x0, + "MBMS Neighbouring Cell Information Support (MNCI Support)", HFILL } + }, + { &hf_gsm_a_rr_amr_config, + { "AMR Configuration", "gsm_a.rr.amr_config", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bcch_change_mark, + { "BCCH Change Mark", "gsm_a.rr.bcch_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si_change_field, + { "SI Change Field", "gsm_a.rr.si_change_field", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si_change_field_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_si13_change_mark, + { "SI13 Change Mark", "gsm_a.rr.si13_change_mark", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_hsn, + { "HSN", "gsm_a.rr.hsn", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Hopping Sequence Number (HSN)", HFILL } + }, + { &hf_gsm_a_rr_rfl_number, + { "RFL Number", "gsm_a.rr.rfl_number", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Radio Frequency List Number (RFL Number)", HFILL } + }, + { &hf_gsm_a_rr_arfcn_index, + { "ARFCN Index", "gsm_a.rr.arfcn_index", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ma_length, + { "MA Length", "gsm_a.rr.ma_length", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Mobile Allocation Length (MA Length)", HFILL } + }, + { &hf_gsm_a_rr_psi1_repeat_period, + { "PSI1 Repeat Period", "gsm_a.rr.psi1_repeat_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_psi1_repeat_period_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pbcch_pb, + { "Pb", "gsm_a.rr.pbcch_pb", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pbcch_pb_vals), 0x0, + "Power reduction on PBCCH/PCCCH (Pb)", HFILL } + }, + { &hf_gsm_a_rr_pbcch_tsc, + { "TSC", "gsm_a.rr.pbcch_tsc", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Training Sequence Code for PBCCH (TSC)", HFILL } + }, + { &hf_gsm_a_rr_pbcch_tn, + { "TN", "gsm_a.rr.pbcch_tn", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Timeslot Number for PCCH (TN)", HFILL } + }, + { &hf_gsm_a_rr_spgc_ccch_sup, + { "SPGC CCCH Sup", "gsm_a.rr.spgc_ccch_sup", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_spgc_ccch_sup_value), 0x0, + "Split PG Cycle Code on CCCH Support (SPGC CCCH Sup)", HFILL } + }, + { &hf_gsm_a_rr_priority_access_thr, + { "Priority Access Thr", "gsm_a.rr.priority_access_thr", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_priority_access_thr_vals), 0x0, + "Priority Access Threshold for packet access (Priority Access Thr)", HFILL } + }, + { &hf_gsm_a_rr_nmo, + { "NMO", "gsm_a.rr.nmo", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_nmo_vals), 0x0, + "Network mode of Operation (NMO)", HFILL } + }, + { &hf_gsm_a_rr_t3168, + { "T3168", "gsm_a.rr.t3168", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t3168_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_t3192, + { "T3192", "gsm_a.rr.t3192", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t3192_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_drx_timer_max, + { "DRX Timer Max", "gsm_a.rr.drx_timer_max", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_drx_timer_max_vals), 0x0, + "Discontinuous Reception Timer Max (DRX Timer Max)", HFILL } + }, + { &hf_gsm_a_rr_access_burst_type, + { "Access Burst Type", "gsm_a.rr.access_burst_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_access_burst_type_value), 0x0, + "Format used in the PACKET CHANNEL REQUEST message, the PS HANDOVER ACCESS message, the PTCCH uplink block and in the PACKET CONTROL ACKNOWLEDGMENT message (Access Burst Type)", HFILL } + }, + { &hf_gsm_a_rr_control_ack_type, + { "Control Ack Type", "gsm_a.rr.control_ack_type", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_control_ack_type_value), 0x0, + "Default format of the PACKET CONTROL ACKNOWLEDGMENT message (Control Ack Type)", HFILL } + }, + { &hf_gsm_a_rr_bs_cv_max, + { "BS CV Max", "gsm_a.rr.bs_cv_max", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Base Station Countdown Value Maximum (BS CV Max)", HFILL } + }, + { &hf_gsm_a_rr_pan_dec, + { "PAN Dec", "gsm_a.rr.pan_dec", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pan_inc, + { "PAN Inc", "gsm_a.rr.pan_inc", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pan_max, + { "PAN Max", "gsm_a.rr.pan_max", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pan_max_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_packet_channel_request, + { "EGPRS Packet Channel Request", "gsm_a.rr.egprs_packet_channel_request", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_egprs_packet_channel_request_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bep_period, + { "BEP Period", "gsm_a.rr.bep_period", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_bep_period_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pfc_feature_mode, + { "PFC Feature Mode", "gsm_a.rr.pfc_feature_mode", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_pfc_feature_mode_value), 0x0, + "Packet Flow Context Feature Mode (PFC Feature Mode)", HFILL } + }, + { &hf_gsm_a_rr_dtm_support, + { "DTM Support", "gsm_a.rr.dtm_support", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtm_support_value), 0x0, + "Dual Transfer Mode Support (DTM Support)", HFILL } + }, + { &hf_gsm_a_rr_bss_paging_coordination, + { "BSS Paging Coordination", "gsm_a.rr.bss_paging_coordination", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_bss_paging_coordination_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ccn_active, + { "CCN Active", "gsm_a.rr.ccn_active", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_ccn_active_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_nw_ext_utbf, + { "NW Ext UTBF", "gsm_a.rr.nw_ext_utbf", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_nw_ext_utbf_value), 0x0, + "Network Extended Uplink TBF (NW Ext UTBF)", HFILL } + }, + { &hf_gsm_a_rr_multiple_tbf_capability, + { "Multiple TBF Capability", "gsm_a.rr.multiple_tbf_capability", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_multiple_tbf_capability_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ext_utbf_no_data, + { "Ext UTBF No Data", "gsm_a.rr.ext_utbf_no_data", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_ext_utbf_no_data_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_dtm_enhancements_capability, + { "DTM Enhancements Capability", "gsm_a.rr.dtm_enhancements_capability", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_dtm_enhancements_capability_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reduced_latency_access, + { "Reduced Latency Access", "gsm_a.rr.reduced_latency_access", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_reduced_latency_access_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_alpha, + { "Alpha", "gsm_a.rr.alpha", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_alpha_vals), 0x0, + "Alpha parameter for GPR MS output power control (Alpha)", HFILL } + }, + { &hf_gsm_a_rr_t_avg_w, + { "T Avg W", "gsm_a.rr.t_avg_w", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t_avg_x_vals), 0x0, + "Signal strength filter period for power control in packet idle mode", HFILL } + }, + { &hf_gsm_a_rr_t_avg_t, + { "T Avg T", "gsm_a.rr.t_avg_t", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_t_avg_x_vals), 0x0, + "Signal strength filter period for power control in packet transfer mode", HFILL } + }, + { &hf_gsm_a_rr_pc_meas_chan, + { "PC Meas Chan", "gsm_a.rr.pc_meas_chan", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_pc_meas_chan_value), 0x0, + "Channel used to measure the received power level on the downlink for the purpose of the uplink power control (PC Meas Chan)", HFILL } + }, + { &hf_gsm_a_rr_n_avg_i, + { "N Avg I", "gsm_a.rr.n_avg_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_n_avg_i_vals), 0x0, + "Interfering signal strength filter constant for power control (N Avg I)", HFILL } + }, + { &hf_gsm_a_rr_sgsnr, + { "SGSNR", "gsm_a.rr.sgsnr", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_sgsnr_value), 0x0, + "SGSN Release (SGSNR)", HFILL } + }, + { &hf_gsm_a_rr_si_status_ind, + { "SI Status Ind", "gsm_a.rr.si_status_ind", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_si_status_ind_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_lb_ms_txpwr_max_cch, + { "LB MS TxPwr Max CCCH", "gsm_a.rr.n_avg_i", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_n_avg_i_vals), 0x0, + "Maximum TX power level an MS is allowed to use on all other than DCS 1800 and PCS 1900 frequency bands when accessing the system until otherwise commanded (LB MS TxPwr Max CCCH)", HFILL } + }, + { &hf_gsm_a_rr_si2n_support, + { "SI2n Support", "gsm_a.rr.si2n_support", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_si2n_support_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mi_index, + { "Measurement Information Index", "gsm_a.rr.mi_index", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mi_count, + { "Measurement Information Count", "gsm_a.rr.mi_count", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_wait, + { "3G Wait", "gsm_a.rr.3g_wait", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_3g_wait_vals), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c, + { "Qsearch C", "gsm_a.rr.qsearch_c", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_x_vals), 0x00, + "Search for 3G cells if signal level is below (0 7) or above (8 15) threshold (Qsearch C)", HFILL } + }, + { &hf_gsm_a_rr_bsic_seen, + { "BSIC Seen", "gsm_a.rr.bsic_seen", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_bsic_seen_value), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_scale, + { "Scale", "gsm_a.rr.scale", + FT_BOOLEAN, BASE_NONE, TFS(&gsm_a_rr_scale_value), 0x0, + "Offset applied for the reported RXLEV values (Scale)", HFILL } + }, + { &hf_gsm_a_rr_mean_bep_gmsk, + { "Mean BEP GMSK", "gsm_a.rr.mean_bep_gmsk", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_mean_bep_gmsk_vals), 0x00, + "Mean Bit Error Probability in GMSK (Mean BEP GMSK)", HFILL } + }, + { &hf_gsm_a_rr_mean_cv_bep, + { "CV BEP", "gsm_a.rr.cv_bep", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_cv_bep_vals), 0x00, + "Coefficient of Variation of the Bit Error Probability (CV BEP)", HFILL } + }, + { &hf_gsm_a_rr_nbr_rcvd_blocks, + { "Nb Rcvd Blocks", "gsm_a.rr.nbr_rcvd_blocks", + FT_UINT8, BASE_DEC, NULL, 0x00, + "Number of correctly decoded blocks that were completed during the measurement report period (Nb Rcvd Blocks)", HFILL } + }, + { &hf_gsm_a_rr_reporting_quantity, + { "Reporting Quantity", "gsm_a.rr.reporting_quantity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_utran_start, + { "UTRAN Start", "gsm_a.rr.3g_priority.utran_start", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_start), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_utran_stop, + { "UTRAN Stop", "gsm_a.rr.3g_priority.utran_stop", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_stop), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_utran_prio, + { "DEFAULT_UTRAN_PRIORITY", "gsm_a.rr.3g_priority.default_utran_prio", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_threshold_utran, + { "DEFAULT_THRESH_UTRAN", "gsm_a.rr.3g_priority.default_threshold_utran", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_priority_param_desc_default_utran_qrxlevmin, + { "DEFAULT_UTRAN_QRXLEVMIN", "gsm_a.rr.3g_priority.default_utran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_utran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_frequency_index, + { "UTRAN Frequency Index", "gsm_a.rr.3g_priority.utran_frequency_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_priority, + { "UTRAN_PRIORITY", "gsm_a.rr.3g_priority.utran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_utran_high, + { "THRESH_UTRAN_high", "gsm_a.rr.3g_priority.thres_utran_high", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_utran_low, + { "THRESH_UTRAN_low", "gsm_a.rr.3g_priority.thres_utran_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_qrxlevmin, + { "UTRAN_QRXLEVMIN", "gsm_a.rr.3g_priority.utran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_utran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_ccn_active, + { "E-UTRAN_CCN_ACTIVE", "gsm_a.rr.3g_priority.eutran_ccn_active", + FT_BOOLEAN, BASE_DEC, TFS(&eutran_ccn_active), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_start, + { "E-UTRAN Start", "gsm_a.rr.3g_priority.eutran_start", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_start), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_stop, + { "E-UTRAN Stop", "gsm_a.rr.3g_priority.utran_stop", + FT_BOOLEAN, BASE_DEC, TFS(&priority_utran_stop), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_c_eutran_initial, + { "Qsearch_C_E-UTRAN_Initial", "gsm_a.rr.qsearch_c_eutran_initial", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_qsearch_c_eutran_initial), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_rep_quant, + { "E-UTRAN_REP_QUANT", "gsm_a.rr.eutran_rep_quant", + FT_BOOLEAN, BASE_DEC, TFS(&eutran_rep_quant), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_multirat_reporting, + { "E-UTRAN_MULTIRAT_REPORTING (nb of E-UTRAN cells to be included in measurement report)", "gsm_a.rr.eutran_multirat_reporting", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrp, + { "E-UTRAN_FDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_fdd_reporting_threshold_rsrp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrp_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_rsrq, + { "E-UTRAN_FDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_fdd_reporting_threshold_rsrq", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrq_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_threshold_2, + { "E-UTRAN_FDD_REPORTING_THRESHOLD_2", "gsm_a.rr.eutran_fdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_reporting_offset, + { "E-UTRAN_FDD_REPORTING_OFFSET", "gsm_a.rr.eutran_fdd_reporting_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrp, + { "E-UTRAN_TDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_tdd_reporting_threshold_rsrp", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrp_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_rsrq, + { "E-UTRAN_TDD_REPORTING_THRESHOLD", "gsm_a.rr.eutran_tdd_reporting_threshold_rsrq", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_lte_rsrq_reporting_threshold_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_threshold_2, + { "E-UTRAN_TDD_REPORTING_THRESHOLD_2", "gsm_a.rr.eutran_tdd_reporting_threshold_2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_reporting_offset, + { "E-UTRAN_TDD_REPORTING_OFFSET", "gsm_a.rr.eutran_tdd_reporting_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_fdd_measurement_report_offset, + { "E-UTRAN_FDD_MEASUREMENT_REPORT_OFFSET", "gsm_a.rr.eutran_fdd_measurement_report_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_tdd_measurement_report_offset, + { "E-UTRAN_TDD_MEASUREMENT_REPORT_OFFSET", "gsm_a.rr.eutran_tdd_measurement_report_offset", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_reporting_granularity, + { "REPORTING_GRANULARITY", "gsm_a.rr.reporting_granularity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_default_measurement_control_eutran, + { "DEFAULT_Measurement_Control_E-UTRAN", "gsm_a.rr.eutran_default_measurement_control_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_measurement_control_eutran, + { "Measurement_Control_E-UTRAN", "gsm_a.rr.eutran_measurement_control_eutran", + FT_UINT8, BASE_DEC, TFS(&measurement_control_eutran), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_qsearch_p_eutran, + { "Qsearch_P_E-UTRAN", "gsm_a.rr.qsearch_p_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_geran_priority, + { "GERAN_PRIORITY", "gsm_a.rr.serving_cell_priority_param_geran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_thresh_prio_search, + { "THRESH_Priority_Search", "gsm_a.rr.serving_cell_priority_param_thresh_prio_search", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_thresh_priority_search), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_thresh_gsm_low, + { "THRESH_GSM_low", "gsm_a.rr.serving_cell_priority_param_thresh_gsm_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_thresh_gsm_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_h_prio, + { "H_PRIO", "gsm_a.rr.serving_cell_priority_param_h_prio", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_priority_param_h_prio), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_serving_cell_priority_param_t_reselection, + { "T_Reselection", "gsm_a.rr.serving_cell_priority_param_t_reselection", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_serving_cell_priority_param_t_reselection), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_earfcn, + { "EARFCN", "gsm_a.rr.earfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_measurement_bandwidth, + { "Measurement Bandwidth", "gsm_a.rr.eutran_measurement_bandwidth", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_eutran_measurement_bandwidth), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_priority, + { "E-UTRAN_PRIORITY", "gsm_a.rr.eutran_priority", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_eutran_high, + { "THRESH_EUTRAN_high", "gsm_a.rr.thresh_eutran_high", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_thresh_eutran_low, + { "THRESH_EUTRAN_low", "gsm_a.rr.thresh_eutran_low", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_thresh_utran_eutran_high_low), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_qrxlevmin, + { "E-UTRAN_QRXLEVMIN", "gsm_a.rr.eutran_qrxlevmin", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_eutran_qrxlevmin), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid, + { "PCID", "gsm_a.rr.pcid", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_bitmap_group, + { "PCID_BITMAP_GROUP", "gsm_a.rr.pcid_bitmap_group", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern_length, + { "PCID_Pattern_length", "gsm_a.rr.pcid_pattern_length", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pcid_psc_pattern_length), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern, + { "PCID_Pattern", "gsm_a.rr.pcid_pattern", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_pcid_pattern_sense, + { "PCID_pattern_sense", "gsm_a.rr.pcid_pattern_sense", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_rr_pcid_pattern_sense), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_frequency_index, + { "E-UTRAN_FREQUENCY_INDEX", "gsm_a.rr.eutran_frequency_index", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_psc, + { "PSC", "gsm_a.rr.psc", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_psc_pattern_length, + { "PSC_Pattern_length", "gsm_a.rr.psc_pattern_length", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_pcid_psc_pattern_length), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_psc_pattern_sense, + { "PSC_pattern_sense", "gsm_a.rr.psc_pattern_sense", + FT_BOOLEAN, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_csg_fdd_uarfcn, + { "CSG FDD UARFCN", "gsm_a.rr.utran_csg_fdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_utran_csg_tdd_uarfcn, + { "CSG TDD UARFCN", "gsm_a.rr.utran_csg_tdd_uarfcn", + FT_UINT16, BASE_DEC, NULL, 0x0000, + NULL, HFILL } + }, + { &hf_gsm_a_rr_csg_earfcn, + { "CSG_EARFCN", "gsm_a.rr.csg_earfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_3g_control_param_desc_meas_ctrl_utran, + { "PCID_pattern_sense", "gsm_a.rr._meas_ctrl_utran", + FT_BOOLEAN, BASE_DEC, TFS(&measurement_control_utran), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_extended_ra, + { "Extended_RA", "gsm_a.rr.extended_ra", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_access_tech_type, + { "Access_Technology_Type", "gsm_a.rr.access_tech_type", + FT_UINT8, BASE_DEC, VALS(gsm_a_access_tech_type_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tfi_assignment, + { "TFI_Assignment", "gsm_a.rr.tfi_assignment", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_polling, + { "Polling", "gsm_a.rr.polling", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_polling_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_usf, + { "USF", "gsm_a.rr.usf", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_usf_granularity, + { "USF_granularity", "gsm_a.rr.usf_granularity", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_usf_granularity_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_p0, + { "P0", "gsm_a.rr.p0", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pr_mode, + { "pr_mode", "gsm_a.rr.pr_mode", + FT_BOOLEAN, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_mcs, + { "Egprs_Modulation_and_Coding_Scheme", "gsm_a.rr.egprs_mcs", + FT_UINT8, BASE_DEC, VALS(gsm_a_egprs_mcs_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tlli_block_channel_coding, + { "TLLI_Block_Channel_Coding", "gsm_a.rr.tlli_block_cs", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_tlli_block_channel_coding_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_bep_period2, + { "Bep_Period2", "gsm_a.rr.bep_period2", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_resegment, + { "Resegment", "gsm_a.rr.resegment", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_resegment_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_egprs_window_size, + { "Egprs_Windows_Size", "gsm_a.rr.egprs_win_size", + FT_UINT8, BASE_DEC, VALS(gsm_a_egprs_windows_size_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gamma, + { "Gamma", "gsm_a.rr.gamma", + FT_UINT8, BASE_DEC, VALS(gsm_a_rr_gamma_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv_index, + { "Timing_Advance_Index", "gsm_a.rr.timing_adv_idx", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_tbf_starting_time, + { "TBF_Starting_Time", "gsm_a.rr.tvf_start_time", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_num_of_radio_block_allocated, + { "Number_of_Radio_Block_Allocated", "gsm_a.rr.num_of_radio_blk_allocated", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_pfi, + { "PFI", "gsm_a.rr.pfi", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_mbms_service_id, + { "MBMS_Service_ID", "gsm_a.rr.mbms_service_id", + FT_UINT32, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ms_id, + { "MS_ID", "gsm_a.rr.ms_id", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_timing_adv_timeslot_num, + { "Timing_Advance_Timeslot_Number", "gsm_a.rr.timing_adv_ts", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_gprs_cs, + { "Channel_Coding_Command", "gsm_a.rr.gprs_cs", + FT_UINT8, BASE_DEC, VALS(gsm_a_gprs_cs_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_rlc_mode, + { "RLC_Mode", "gsm_a.rr.rlc_mode", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_rlc_mode_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ta_valid, + { "TA_Valid", "gsm_a.rr.ta_valid", + FT_BOOLEAN, BASE_DEC, TFS(&gsm_a_ta_valid_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_link_quality_meas_mode, + { "Link_Quality_Measure_Mode", "gsm_a.rr.link_qual_meas_mode", + FT_UINT8, BASE_DEC, VALS(&gsm_a_link_quality_meas_mode_vals), 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_emr_bitmap_length, + { "BITMAP_LENGTH", "gsm_a.rr.emr_bitmap_len", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_n_eutran, + { "N_E-UTRAN", "gsm_a.rr.eutran_mr_n_eutran", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_freq_idx, + { "E-UTRAN_FREQUENCY_INDEX", "gsm_a.rr.eutran_mr_freq_idx", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_cell_id, + { "CELL IDENTITY", "gsm_a.rr.eutran_mr_cell_id", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_eutran_mr_rpt_quantity, + { "REPORTING_QUANTITY", "gsm_a.rr.eutran_mr_rpt_quantity", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_gsm_a_rr_ma_channel_set, + { "Channel Set", "gsm_a.rr.ma_channel_set", + FT_UINT8, BASE_HEX, NULL, 0x00, + NULL, HFILL } + }, + { &hf_n_range_orig_arfcn, + { "ORIG-ARFCN", "gsm_a.rr.orig_arfcn", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + }; + + static hf_register_info hf_rr_short_pd[] = + { + { &hf_gsm_a_rr_short_pd, + { "Radio Resources Short Protocol Discriminator", "gsm_a.rr_short_pd", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_short_pd_msg_type, + { "Radio Resources Short PD Message Type", "gsm_a.rr_short_pd_type", + FT_UINT8, BASE_HEX, VALS(gsm_a_rr_short_pd_msg_strings), 0x0, + NULL, HFILL } + }, + { &hf_gsm_a_rr_short_l2_header, + { "Radio Resources Short L2 Header", "gsm_a.rr_short_l2_header", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL } + } + }; + + /* Setup protocol subtree array */ +#define NUM_INDIVIDUAL_ELEMS 3 + gint *ett[NUM_INDIVIDUAL_ELEMS + + NUM_GSM_DTAP_MSG_RR + + NUM_GSM_RR_ELEM + + NUM_GSM_RR_REST_OCTETS_ELEM + + NUM_GSM_SACCH_MSG_RR]; + + ett[0] = &ett_ccch_msg; + ett[1] = &ett_ccch_oct_1; + ett[2] = &ett_sacch_msg; + + last_offset = NUM_INDIVIDUAL_ELEMS; + + for (i=0; i < NUM_GSM_DTAP_MSG_RR; i++, last_offset++) + { + ett_gsm_dtap_msg_rr[i] = -1; + ett[last_offset] = &ett_gsm_dtap_msg_rr[i]; + } + + for (i=0; i < NUM_GSM_RR_ELEM; i++, last_offset++) + { + ett_gsm_rr_elem[i] = -1; + ett[last_offset] = &ett_gsm_rr_elem[i]; + } + + for (i=0; i < NUM_GSM_RR_REST_OCTETS_ELEM; i++, last_offset++) + { + ett_gsm_rr_rest_octets_elem[i] = -1; + ett[last_offset] = &ett_gsm_rr_rest_octets_elem[i]; + } + + for (i=0; i < NUM_GSM_SACCH_MSG_RR; i++, last_offset++) + { + ett_gsm_sacch_msg_rr[i] = -1; + ett[last_offset] = &ett_gsm_sacch_msg_rr[i]; + } + + /* Register the protocol name and description */ + proto_a_ccch = + proto_register_protocol("GSM CCCH", "GSM CCCH", "gsm_a_ccch"); + + proto_register_field_array(proto_a_ccch, hf, array_length(hf)); + + /* subdissector code */ + register_dissector("gsm_a_ccch", dissect_ccch, proto_a_ccch); + + /* Register the protocol name and description */ + proto_a_sacch = + proto_register_protocol("GSM SACCH", "GSM SACCH", "gsm_a_sacch"); + + proto_register_field_array(proto_a_sacch, hf_rr_short_pd, array_length(hf_rr_short_pd)); + + /* subdissector code */ + register_dissector("gsm_a_sacch", dissect_sacch, proto_a_sacch); + + /* subtree array (for both sub-dissectors) */ + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_gsm_a_rr(void) +{ + data_handle = find_dissector("data"); + rrc_irat_ho_info_handle = find_dissector("rrc.irat.irat_ho_info"); + rrc_irat_ho_to_utran_cmd_handle = find_dissector("rrc.irat.ho_to_utran_cmd"); + rrlp_dissector = find_dissector("rrlp"); +}