Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers cipv6_fragmenter.c Source File

cipv6_fragmenter.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2013-2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 /**
00018  *
00019  * \file cipv6_fragmenter.c
00020  * \brief Packet Fragmentation and Reassembly.
00021  *
00022  */
00023 
00024 #include "nsconfig.h"
00025 #include "ns_types.h"
00026 #include "string.h"
00027 #include "ns_trace.h"
00028 #include "randLIB.h"
00029 #include "Core/include/socket.h"
00030 #include "6LoWPAN/IPHC_Decode/cipv6.h"
00031 #include "6LoWPAN/Fragmentation/cipv6_fragmenter.h"
00032 #include "NWK_INTERFACE/Include/protocol.h"
00033 #include "nsdynmemLIB.h"
00034 #include "6LoWPAN/Mesh/mesh.h"
00035 #include "6LoWPAN/IPHC_Decode/iphc_decompress.h"
00036 #include "nwk_stats_api.h"
00037 #include "NWK_INTERFACE/Include/protocol_stats.h"
00038 #include "common_functions.h"
00039 #include "6LoWPAN/MAC/mac_helper.h"
00040 
00041 #define TRACE_GROUP "6frg"
00042 
00043 typedef struct {
00044     uint16_t ttl;   /*!< Reassembly timer (seconds) */
00045     uint16_t tag;   /*!< Fragmentation datagram TAG ID */
00046     uint16_t size;  /*!< Datagram Total Size (uncompressed) */
00047     uint16_t orig_size; /*!< Datagram Original Size (compressed) */
00048     uint16_t frag_max;  /*!< Maximum fragment size (MAC payload) */
00049     uint16_t offset; /*!< Data offset from datagram start */
00050     int16_t pattern; /*!< Size of compressed LoWPAN headers */
00051     buffer_t *buf;
00052     ns_list_link_t      link; /*!< List link entry */
00053 } reassembly_entry_t;
00054 
00055 typedef NS_LIST_HEAD (reassembly_entry_t, link) reassembly_list_t;
00056 
00057 typedef struct {
00058     int8_t interface_id;
00059     uint16_t timeout;
00060     reassembly_list_t rx_list;
00061     reassembly_list_t free_list;
00062     reassembly_entry_t *entry_pointer_buffer;
00063     ns_list_link_t      link; /*!< List link entry */
00064 } reassembly_interface_t;
00065 
00066 static NS_LIST_DEFINE(reassembly_interface_list, reassembly_interface_t, link);
00067 
00068 
00069 /* Reassembly structures and helpers - basically the same as in
00070  * ipv6_fragmentation.c, as we are also using a variation of RFC 815, but there
00071  * are enough minor differences that it doesn't seem worth trying to share code.
00072  */
00073 
00074 /* We reassemble into the datagram buffer in basically the style of RFC 815 */
00075 /* An 6-byte hole descriptor is placed directly in buffer holes */
00076 /* We link them them by buffer offset (relative to start of fragmentable section) */
00077 /* Note the possible need to align means we can't use more than 7 bytes */
00078 typedef struct hole {
00079     uint16_t first;
00080     uint16_t last;
00081     uint16_t next;
00082 } hole_t;
00083 
00084 /* Given the offset of a hole in the datagram buffer, return an aligned pointer
00085  * to put a hole_t in it. We assume a "normal" platform requiring 2-byte
00086  * alignment for hole_t, and letting us manipulate uintptr_t in the conventional
00087  * fashion.
00088  */
00089 static hole_t *hole_pointer(const buffer_t *buf, uint16_t offset)
00090 {
00091     uintptr_t ptr = (uintptr_t)(buffer_data_pointer(buf) + offset);
00092 
00093     return (hole_t *)((ptr + 1) & ~(uintptr_t) 1);
00094 }
00095 
00096 static void delete_hole(buffer_t *buf, uint16_t hole, uint16_t *prev_ptr)
00097 {
00098     hole_t *hole_ptr = hole_pointer(buf, hole);
00099 
00100     *prev_ptr = hole_ptr->next;
00101 }
00102 
00103 static hole_t *create_hole(buffer_t *buf, uint16_t first, uint16_t last, uint16_t *prev_ptr)
00104 {
00105     hole_t *hole_ptr = hole_pointer(buf, first);
00106     hole_ptr->first = first;
00107     hole_ptr->last = last;
00108     hole_ptr->next = *prev_ptr;
00109 
00110     *prev_ptr = first;
00111     return hole_ptr;
00112 }
00113 
00114 /*
00115  * RFC 4944 is oddly designed - it has blurred the header compression
00116  * and fragmentation layers. The datagram_size and datagram_offset field are
00117  * specified in terms of the uncompressed IPv6 datagram, not the actual 6LoWPAN
00118  * payload.
00119  *
00120  * This complicates reassembly if you don't decompress first; we don't because
00121  * the original upper layer works on IPHC headers directly, rather than native
00122  * IPv6.
00123  *
00124  * To handle the general case, including arbitrary fragment order so we don't
00125  * always know the 6LoWPAN size of the first fragment, the reassembly buffer
00126  * always leaves space for the uncompressed IPv6 header, and 1 byte more for a
00127  * 6LoWPAN "uncompressed IPv6" dispatch byte. This means non-first fragments
00128  * are always placed at buffer_data_pointer() + datagram_offset.
00129  *
00130  * This routine doesn't explicitly distinguish the compressed and uncompressed
00131  * cases - the difference arises purely from the output of "iphc_header_scan",
00132  * which sets "pattern" to the difference between IPv6 and 6LoWPAN size -
00133  * but to aid understanding, here's what it ends up doing in the two cases:
00134  *
00135  * IPHC compressed case
00136  * --------------------
00137  *
00138  *   -4         0                0x50
00139  *    +---------+--------+--------+       0x50 bytes of 6LoWPAN data
00140  *    |  FRAG1  |  IPHC  |  data1 |       0x70 bytes of uncompressed IPv6 data
00141  *    +---------+--------+--------+       "pattern" = 0x70 - 0x50 = 0x20
00142  *
00143  *   -5          0            0x60        (datagram_size = 0xD0)
00144  *    +----------+-------------+
00145  *    |FRAGN 0x70|    data2    |
00146  *    +----------+-------------+
00147  *
00148  * During assembly, the data pointer points at the "0" position representing the
00149  * virtual start of the IPv6 packet:
00150  *
00151  *   -1 0        0x20             0x70           0xD0
00152  *    +-+---------+--------+--------+-------------+
00153  *    | | padding |  IPHC  |  data1 |    data2    |
00154  *    +-+---------+--------+--------+-------------+
00155  *
00156  * On completion of assembly, the start pointer moves forward to point at the
00157  * IPHC header. (This means buffer size is slightly inefficient for an IPHC
00158  * upper layer, but it does reserve headroom for decompression to native IPv6.)
00159  *
00160  *  -0x21         0               0x50           0xB0
00161  *    +-----------+--------+--------+-------------+
00162  *    | headroom  |  IPHC  |  data1 |    data2    |
00163  *    +-----------+--------+--------+-------------+
00164 
00165  * Uncompressed case
00166  * -----------------
00167 
00168  *   -4         0 1             0x71  D = "Uncompressed IPv6" dispatch type 0x41
00169  *    +---------+-+--------------+    0x71 bytes of 6LoWPAN data
00170  *    |  FRAG1  |D|    data1     |    0x70 bytes of uncompressed IPv6 data
00171  *    +---------+-+--------------+    "pattern" = 0x70 - 0x71 = -1
00172  *
00173  *    -5         0              0x60        (datagram_size = 0xD0)
00174  *    +----------+---------------+
00175  *    |FRAGN 0x70|     data2     |
00176  *    +----------+---------------+
00177  *
00178  * During assembly, the data pointer points at the "0" position representing the
00179  * start of the IPv6 packet:
00180  *
00181  *   -1 0             0x70            0xD0
00182  *    +-+--------------+---------------+
00183  *    |D|    data1     |     data2     |
00184  *    +-+--------------+---------------+
00185  *
00186  * On completion of assembly, the start pointer moves back to point to the
00187  * 6LoWPAN dispatch byte:
00188  *
00189  *    0 1             0x71            0xD1
00190  *    +-+--------------+---------------+
00191  *    |D|    data1     |     data2     |
00192  *    +-+--------------+---------------+
00193 
00194  * (And if it's neither of these cases, a "native" 6LoWPAN reassembly happens
00195  * with "pattern" set to 0 - what probably should have happened in the first
00196  * place; offsets are treated as 6LoWPAN offsets from the start of the
00197  * fragmented 6LoWPAN data).
00198  */
00199 
00200 //Discover
00201 static reassembly_interface_t *reassembly_interface_discover(int8_t interfaceId)
00202 {
00203 
00204     ns_list_foreach(reassembly_interface_t, interface_ptr, &reassembly_interface_list) {
00205         if (interfaceId == interface_ptr->interface_id) {
00206             return interface_ptr;
00207         }
00208     }
00209 
00210     return NULL;
00211 }
00212 
00213 static void reassembly_entry_free(reassembly_interface_t *interface_ptr, reassembly_entry_t *entry)
00214 {
00215     ns_list_remove(&interface_ptr->rx_list, entry);
00216     ns_list_add_to_start(&interface_ptr->free_list, entry);
00217     if (entry->buf) {
00218         entry->buf = buffer_free(entry->buf);
00219     }
00220 }
00221 
00222 static void reassembly_list_free(reassembly_interface_t *interface_ptr )
00223 {
00224     ns_list_foreach_safe(reassembly_entry_t, reassembly_entry, &interface_ptr->rx_list) {
00225         reassembly_entry_free(interface_ptr, reassembly_entry);
00226     }
00227 }
00228 
00229 
00230 static reassembly_entry_t *reassembly_already_action(reassembly_list_t *reassembly_list, buffer_t *buf, uint16_t tag, uint16_t size)
00231 {
00232     ns_list_foreach(reassembly_entry_t, reassembly_entry, reassembly_list) {
00233         if ((reassembly_entry->tag == tag) && (reassembly_entry->size == size) &&
00234                 reassembly_entry->buf->src_sa.addr_type == buf->src_sa .addr_type  &&
00235                 reassembly_entry->buf->dst_sa.addr_type == buf->dst_sa .addr_type ) {
00236             /* Type will be either long or short 802.15.4 - we skip the PAN ID */
00237             if (memcmp(reassembly_entry->buf->src_sa.address + 2, buf->src_sa .address  + 2, addr_len_from_type(buf->src_sa .addr_type ) - 2) == 0 &&
00238                     memcmp(reassembly_entry->buf->dst_sa.address + 2, buf->dst_sa .address  + 2, addr_len_from_type(buf->dst_sa .addr_type ) - 2) == 0) {
00239                 return reassembly_entry;
00240             }
00241         }
00242     }
00243 
00244     return NULL;
00245 
00246 }
00247 
00248 static reassembly_entry_t *lowpan_adaptation_reassembly_get(reassembly_interface_t *interface_ptr)
00249 {
00250     reassembly_entry_t *entry = ns_list_get_first(&interface_ptr->free_list);
00251     if (!entry) {
00252         return NULL;
00253     }
00254 
00255     ns_list_remove(&interface_ptr->free_list, entry);
00256     memset(entry, 0, sizeof(reassembly_entry_t));
00257     //Add to first
00258     ns_list_add_to_start(&interface_ptr->rx_list, entry);
00259 
00260     return entry;
00261 }
00262 
00263 
00264 buffer_t *cipv6_frag_reassembly(int8_t interface_id, buffer_t *buf)
00265 {
00266     reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
00267     if (!interface_ptr) {
00268         return buffer_free(buf);
00269     }
00270 
00271     uint16_t datagram_size, datagram_tag;
00272     uint16_t fragment_first;
00273     uint8_t frag_header;
00274 
00275     uint8_t *ptr = buffer_data_pointer(buf);
00276 
00277     frag_header = ptr[0];
00278     datagram_size = common_read_16_bit(ptr) & 0x07FF;
00279 
00280     if (datagram_size == 0) {
00281         goto resassembly_error;
00282     }
00283 
00284     ptr += 2;
00285     datagram_tag = common_read_16_bit(ptr);
00286     ptr += 2;
00287     if (frag_header & LOWPAN_FRAGN_BIT) {
00288         fragment_first = *ptr++ << 3;
00289     } else {
00290         fragment_first = 0;
00291     }
00292 
00293     /* Consume the fragment header. We don't distinguish FRAG1/FRAGN after this
00294      * point (we treat FRAGN with offset 0 the same as FRAG1)
00295      */
00296     buffer_data_pointer_set(buf, ptr);
00297     reassembly_entry_t *frag_ptr = reassembly_already_action(&interface_ptr->rx_list, buf, datagram_tag, datagram_size);
00298 
00299     if (!frag_ptr) {
00300 
00301         frag_ptr = lowpan_adaptation_reassembly_get(interface_ptr);
00302         if (!frag_ptr) {
00303             goto resassembly_error;
00304         }
00305 
00306         buffer_t *reassembly_buffer = buffer_get(1 + ((datagram_size+7) & ~7));
00307         if (!reassembly_buffer) {
00308             //Put allocated back to free
00309             reassembly_entry_free(interface_ptr, frag_ptr);
00310             goto resassembly_error;
00311         }
00312 
00313         // Allocate the reassembly buffer.
00314         // Allow 1 byte extra for an "Uncompressed IPv6" dispatch byte - the
00315         // 6LoWPAN data can be 1 byte longer than the IPv6 data.
00316         // Also, round datagram size up to a multiple of 8 to ensure we have
00317         // room for a final hole descriptor (it can spill past the indicated
00318         // datagram size if the last fragment is smaller than 8 bytes).
00319 
00320         reassembly_buffer->src_sa  = buf->src_sa ;
00321         reassembly_buffer->dst_sa  = buf->dst_sa ;
00322         frag_ptr->ttl = interface_ptr->timeout;
00323         frag_ptr->tag = datagram_tag;
00324         frag_ptr->size = datagram_size;
00325         // Set buffer length and adjust start pointer, so it represents the
00326         // uncompressed IPv6 packet. (See comment block before this function).
00327         buffer_data_length_set(reassembly_buffer, 1 + datagram_size);
00328         buffer_data_strip_header(reassembly_buffer, 1);
00329         // Write initial hole descriptor into buffer
00330         frag_ptr->offset = 0xffff;
00331         create_hole(reassembly_buffer, 0, datagram_size - 1, &frag_ptr->offset );
00332         frag_ptr->buf = reassembly_buffer;
00333     }
00334 
00335     /* For the first link fragment, work out and remember the "pattern"
00336      * (difference between6LoWPAN and IPv6 size), and also copy the buffer
00337      * header metadata.
00338      */
00339     uint16_t lowpan_size, ipv6_size;
00340     if (fragment_first == 0) {
00341         uint16_t uncompressed_header_size;
00342         uint8_t compressed_header_size;
00343         compressed_header_size = iphc_header_scan(buf, &uncompressed_header_size);
00344         lowpan_size = buffer_data_length(buf);
00345         ipv6_size = lowpan_size - compressed_header_size + uncompressed_header_size;
00346         frag_ptr->pattern = ipv6_size - lowpan_size;
00347 
00348         /* Clone the buffer header from this first fragment, preserving only size + pointers */
00349         /* Also the security flag - this fragment's flag is merged in later */
00350         bool buf_security = frag_ptr->buf->options.ll_security_bypass_rx;
00351         buffer_copy_metadata(frag_ptr->buf, buf, true);
00352         frag_ptr->buf->options.ll_security_bypass_rx = buf_security;
00353     } else {
00354         ipv6_size = lowpan_size = buffer_data_length(buf);
00355     }
00356 
00357     uint16_t fragment_last = fragment_first + ipv6_size - 1;
00358     if (fragment_last >= datagram_size) {
00359         tr_err("Frag out-of-range: last=%u, size=%u", fragment_last, datagram_size);
00360         //Free Current entry
00361         reassembly_entry_free(interface_ptr, frag_ptr);
00362         goto resassembly_error;
00363     }
00364 
00365     /* Hole-filling algorithm, basically as per RFC 815, but with added
00366      * checks for overlap. The hole list is kept sorted, as per
00367      * ipv6_fragmentation.c, but that's not relevant in this version.
00368      */
00369     uint16_t hole_off = frag_ptr->offset;
00370     uint16_t *prev_ptr = &frag_ptr->offset;
00371     do {
00372         hole_t *hole = hole_pointer(frag_ptr->buf, hole_off);
00373         uint_fast16_t hole_first = hole->first;
00374         uint_fast16_t hole_last = hole->last;
00375 
00376         /* Fragment is beyond this hole - move to next (RFC 815 step 2) */
00377         /* Fragment is before this hole - move to next (RFC 815 step 3) */
00378         if (fragment_first > hole_last || fragment_last < hole_first) {
00379             prev_ptr = &hole->next;
00380             hole_off = hole->next;
00381             continue;
00382         }
00383 
00384         /* If any of the fragment lies outside the hole, it indicates a problem;
00385          * we only expect repeat data from retransmission, so fragments should
00386          * always lie entirely within a hole or existing data, not straddle
00387          * them. If we see this happen then junk existing data, making this the
00388          * first fragment of a new reassembly (RFC 4944).
00389          */
00390         if (fragment_first < hole_first || fragment_last > hole_last) {
00391             tr_err("Frag overlap: hole %"PRIuFAST16"-%"PRIuFAST16", frag %"PRIu16"-%"PRIu16, hole_first, hole_last, fragment_first, fragment_last);
00392             protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
00393             /* Forget previous data by marking as "all hole" */
00394             frag_ptr->offset = 0xffff;
00395             create_hole(frag_ptr->buf, hole_off = hole_first = 0, hole_last = datagram_size - 1, prev_ptr = &frag_ptr->offset);
00396         }
00397 
00398         /* Unhook this hole from the hole list (RFC 815 step 4) */
00399         delete_hole(frag_ptr->buf, hole_off, prev_ptr);
00400 
00401         /* Create a new hole in front if necessary (RFC 815 step 5) */
00402         if (fragment_first > hole_first) {
00403             prev_ptr = &create_hole(frag_ptr->buf, hole_first, fragment_first - 1, prev_ptr)->next;
00404         }
00405 
00406         /* Create a following hole if necessary (RFC 815 step 6) */
00407         if (fragment_last < hole_last) {
00408             create_hole(frag_ptr->buf, fragment_last + 1, hole_last, prev_ptr);
00409         }
00410 
00411         /* Unlike RFC 815, we're now done. We don't allow overlaps, so we finish
00412          * as soon as we identify one hole that it entirely or partially fills */
00413         break;
00414     } while (hole_off != 0xffff);
00415 
00416     /* Hole list updated, can now copy in the fragment data -  to make sure the
00417      * initial fragment goes in the right place we use the end offset, rather
00418      * than the start offset. */
00419     memcpy(buffer_data_pointer(frag_ptr->buf) + fragment_last + 1 - lowpan_size, buffer_data_pointer(buf), lowpan_size);
00420 
00421     /* Combine the "improper security" flags, so reassembled buffer's flag is set if any fragment wasn't secure */
00422     /* XXX should have some sort of overall "merge buffer metadata" routine handling this and whatever else */
00423     frag_ptr->buf->options.ll_security_bypass_rx |= buf->options .ll_security_bypass_rx ;
00424 
00425     /* We've finished with the original fragment buffer */
00426     buf = buffer_free(buf);
00427 
00428     /* Completion check - any holes left? */
00429     if (frag_ptr->offset != 0xffff) {
00430         /* Not yet complete - processing finished on this fragment */
00431         return NULL;
00432     }
00433 
00434     /* No more holes, so our reassembly is complete */
00435     buf = frag_ptr->buf ;
00436     frag_ptr->buf = NULL;
00437     reassembly_entry_free(interface_ptr, frag_ptr);
00438 
00439     /* Buffer start pointer is currently at the "start of uncompressed IPv6
00440      * packet" position. Move it either forwards or backwards to match
00441      * the IPHC data (could be compressed, or uncompressed with added dispatch
00442      * byte).
00443      */
00444     buf->buf_ptr  += frag_ptr->pattern;
00445     buf->info  = (buffer_info_t)(B_DIR_UP | B_FROM_FRAGMENTATION | B_TO_IPV6_TXRX);
00446     return buf;
00447 
00448 resassembly_error:
00449     protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
00450     return buffer_free(buf);
00451 }
00452 
00453 static void reassembly_entry_timer_update(reassembly_interface_t *interface_ptr, uint16_t seconds)
00454 {
00455     ns_list_foreach_safe(reassembly_entry_t, reassembly_entry, &interface_ptr->rx_list) {
00456         if (reassembly_entry->ttl > seconds) {
00457             reassembly_entry->ttl -= seconds;
00458         } else {
00459             protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
00460             tr_debug("Reassembly TO: src %s size %u",
00461                      trace_sockaddr(&reassembly_entry->buf->src_sa, true),
00462                      reassembly_entry->size);
00463             reassembly_entry_free(interface_ptr, reassembly_entry);
00464         }
00465     }
00466 }
00467 
00468 void cipv6_frag_timer(uint16_t seconds)
00469 {
00470     ns_list_foreach(reassembly_interface_t, interface_ptr, &reassembly_interface_list) {
00471         reassembly_entry_timer_update(interface_ptr, seconds);
00472     }
00473 }
00474 
00475 int8_t reassembly_interface_free(int8_t interface_id)
00476 {
00477     //Discover
00478     reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
00479     if (!interface_ptr) {
00480         return -1;
00481     }
00482 
00483     ns_list_remove(&reassembly_interface_list, interface_ptr);
00484 
00485     //Free Dynamic allocated entry buffer
00486     ns_dyn_mem_free(interface_ptr->entry_pointer_buffer);
00487     ns_dyn_mem_free(interface_ptr);
00488 
00489     return 0;
00490 }
00491 
00492 
00493 int8_t reassembly_interface_init(int8_t interface_id, uint8_t reassembly_session_limit, uint16_t reassembly_timeout)
00494 {
00495 
00496     if (!reassembly_session_limit || !reassembly_timeout) {
00497         return -2;
00498     }
00499 
00500     //Remove old interface
00501     reassembly_interface_free(interface_id);
00502 
00503     //Allocate new
00504     reassembly_interface_t *interface_ptr = ns_dyn_mem_alloc(sizeof(reassembly_interface_t));
00505     reassembly_entry_t *reassemply_ptr = ns_dyn_mem_alloc(sizeof(reassembly_entry_t) * reassembly_session_limit);
00506     if (!interface_ptr || !reassemply_ptr) {
00507         ns_dyn_mem_free(interface_ptr);
00508         ns_dyn_mem_free(reassemply_ptr);
00509         return -1;
00510     }
00511 
00512     memset(interface_ptr, 0 ,sizeof(reassembly_interface_t));
00513     interface_ptr->interface_id = interface_id;
00514     interface_ptr->timeout = reassembly_timeout;
00515     interface_ptr->entry_pointer_buffer = reassemply_ptr;
00516     ns_list_init(&interface_ptr->free_list);
00517     ns_list_init(&interface_ptr->rx_list);
00518 
00519     for (uint8_t i=0; i<reassembly_session_limit ; i++) {
00520         ns_list_add_to_end(&interface_ptr->free_list, reassemply_ptr);
00521         reassemply_ptr++;
00522     }
00523 
00524     ns_list_add_to_end(&reassembly_interface_list, interface_ptr);
00525 
00526     return 0;
00527 }
00528 
00529 int8_t reassembly_interface_reset(int8_t interface_id)
00530 {
00531     //Discover
00532     reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
00533     if (!interface_ptr) {
00534         return -1;
00535     }
00536 
00537     //Free Reaasembled queue
00538     reassembly_list_free(interface_ptr);
00539     return 0;
00540 }
00541