diff options
author | David Brodski <david@brodski.eu> | 2010-11-23 22:50:50 +0000 |
---|---|---|
committer | David Brodski <david@brodski.eu> | 2010-11-23 22:50:50 +0000 |
commit | cc47103dd756181a23e032a93aa4cec925819747 (patch) | |
tree | 04c253e0e5a302fc53bfdcd49e1ba2f8bc15f005 | |
parent | 2d8268aefde82aa59895dcd55a5eb80bab7ca7a8 (diff) | |
download | gnunet-cc47103dd756181a23e032a93aa4cec925819747.tar.gz gnunet-cc47103dd756181a23e032a93aa4cec925819747.zip |
fragments are now put together to form a message
-rw-r--r-- | src/transport/plugin_transport_wlan.c | 201 |
1 files changed, 186 insertions, 15 deletions
diff --git a/src/transport/plugin_transport_wlan.c b/src/transport/plugin_transport_wlan.c index ce740e785..de868a427 100644 --- a/src/transport/plugin_transport_wlan.c +++ b/src/transport/plugin_transport_wlan.c | |||
@@ -51,6 +51,10 @@ | |||
51 | 51 | ||
52 | #define DEBUG_wlan GNUNET_NO | 52 | #define DEBUG_wlan GNUNET_NO |
53 | 53 | ||
54 | #define MESSAGE_LENGHT_UNKNOWN -1 | ||
55 | #define NO_MESSAGE_OR_MESSAGE_FINISHED -2 | ||
56 | |||
57 | |||
54 | /** | 58 | /** |
55 | * After how long do we expire an address that we | 59 | * After how long do we expire an address that we |
56 | * learned from another peer if it is not reconfirmed | 60 | * learned from another peer if it is not reconfirmed |
@@ -220,7 +224,7 @@ struct AckQueue | |||
220 | { | 224 | { |
221 | struct AckQueue * next; | 225 | struct AckQueue * next; |
222 | struct AckQueue * prev; | 226 | struct AckQueue * prev; |
223 | int fragment_num; //TODO change it to offset | 227 | int fragment_num; //TODO change it to offset if better |
224 | }; | 228 | }; |
225 | 229 | ||
226 | /** | 230 | /** |
@@ -232,7 +236,7 @@ struct RecQueue | |||
232 | struct RecQueue * next; | 236 | struct RecQueue * next; |
233 | struct RecQueue * prev; | 237 | struct RecQueue * prev; |
234 | uint16_t num; | 238 | uint16_t num; |
235 | char * msg; | 239 | const char * msg; |
236 | uint16_t size; | 240 | uint16_t size; |
237 | }; | 241 | }; |
238 | 242 | ||
@@ -328,7 +332,9 @@ struct Session | |||
328 | //int rec_offset; | 332 | //int rec_offset; |
329 | 333 | ||
330 | /** | 334 | /** |
331 | * size of the message received, -1 means that the size is not known, -2 means no message received | 335 | * size of the message received, |
336 | * MESSAGE_LENGHT_UNKNOWN means that the size is not known, | ||
337 | * NO_MESSAGE_OR_MESSAGE_FINISHED means no message received | ||
332 | */ | 338 | */ |
333 | 339 | ||
334 | int rec_size; | 340 | int rec_size; |
@@ -535,6 +541,14 @@ check_fragment_queue(struct Plugin * plugin); | |||
535 | uint32_t | 541 | uint32_t |
536 | getcrc32(const char *msgbuf, size_t msgbuf_size); | 542 | getcrc32(const char *msgbuf, size_t msgbuf_size); |
537 | 543 | ||
544 | static void | ||
545 | free_rec_frag_queue(struct Session * session); | ||
546 | |||
547 | static void | ||
548 | wlan_process_helper (void *cls, | ||
549 | void *client, | ||
550 | const struct GNUNET_MessageHeader *hdr); | ||
551 | |||
538 | /** | 552 | /** |
539 | * get the next message number, at the moment just a random one | 553 | * get the next message number, at the moment just a random one |
540 | * | 554 | * |
@@ -608,7 +622,7 @@ create_session(struct Plugin *plugin,const uint8_t * addr) | |||
608 | memcpy(queue->content->addr, addr, 6); | 622 | memcpy(queue->content->addr, addr, 6); |
609 | queue->content->message_id_out = get_next_message_id(); | 623 | queue->content->message_id_out = get_next_message_id(); |
610 | queue->content->has_fragment = 0; | 624 | queue->content->has_fragment = 0; |
611 | queue->content->rec_size = -2; | 625 | queue->content->rec_size = NO_MESSAGE_OR_MESSAGE_FINISHED; |
612 | 626 | ||
613 | plugin->session_count++; | 627 | plugin->session_count++; |
614 | return queue->content; | 628 | return queue->content; |
@@ -698,6 +712,9 @@ free_acks (struct FragmentMessage * fm){ | |||
698 | GNUNET_CONTAINER_DLL_remove(fm->head, fm->tail, fq); | 712 | GNUNET_CONTAINER_DLL_remove(fm->head, fm->tail, fq); |
699 | GNUNET_free(fq); | 713 | GNUNET_free(fq); |
700 | } | 714 | } |
715 | //needed? | ||
716 | fm->head = NULL; | ||
717 | fm->tail = NULL; | ||
701 | } | 718 | } |
702 | 719 | ||
703 | //TODO doxigen | 720 | //TODO doxigen |
@@ -940,6 +957,7 @@ check_fragment_queue (struct Plugin * plugin){ | |||
940 | } | 957 | } |
941 | } | 958 | } |
942 | 959 | ||
960 | //TODO doxigen | ||
943 | static void | 961 | static void |
944 | check_finished_fragment(struct Plugin * plugin, struct FragmentMessage * fm){ | 962 | check_finished_fragment(struct Plugin * plugin, struct FragmentMessage * fm){ |
945 | struct AckQueue * ack; | 963 | struct AckQueue * ack; |
@@ -1013,7 +1031,7 @@ do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1013 | *(plugin->env->our_hello)); | 1031 | *(plugin->env->our_hello)); |
1014 | 1032 | ||
1015 | msgheader = GNUNET_malloc(size); | 1033 | msgheader = GNUNET_malloc(size); |
1016 | msgheader->size = htons(size - sizeof(struct GNUNET_MessageHeader)); | 1034 | msgheader->size = htons(size); |
1017 | msgheader->type = htons(GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA); | 1035 | msgheader->type = htons(GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA); |
1018 | 1036 | ||
1019 | radioHeader = (struct RadiotapHeader *) &msgheader[1]; | 1037 | radioHeader = (struct RadiotapHeader *) &msgheader[1]; |
@@ -1023,7 +1041,7 @@ do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1023 | getWlanHeader(ieeewlanheader); | 1041 | getWlanHeader(ieeewlanheader); |
1024 | 1042 | ||
1025 | msgheader2 = (struct GNUNET_MessageHeader *) &ieeewlanheader[1]; | 1043 | msgheader2 = (struct GNUNET_MessageHeader *) &ieeewlanheader[1]; |
1026 | msgheader2->size = htons(GNUNET_HELLO_size(*(plugin->env->our_hello))); | 1044 | msgheader2->size = htons(GNUNET_HELLO_size(*(plugin->env->our_hello)) + sizeof(struct GNUNET_MessageHeader)); |
1027 | msgheader2->type = htons(GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT); | 1045 | msgheader2->type = htons(GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT); |
1028 | 1046 | ||
1029 | memcpy(&msgheader2[1], *plugin->env->our_hello, GNUNET_HELLO_size( | 1047 | memcpy(&msgheader2[1], *plugin->env->our_hello, GNUNET_HELLO_size( |
@@ -1103,7 +1121,7 @@ do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1103 | //or the missing part of the message in case this is the last fragment | 1121 | //or the missing part of the message in case this is the last fragment |
1104 | copysize = GNUNET_MIN(fm->message_size - copyoffset, | 1122 | copysize = GNUNET_MIN(fm->message_size - copyoffset, |
1105 | WLAN_MTU - sizeof(struct FragmentationHeader)); | 1123 | WLAN_MTU - sizeof(struct FragmentationHeader)); |
1106 | fragheader.header.size = htons(copysize); | 1124 | fragheader.header.size = htons(copysize + sizeof(struct FragmentationHeader)); |
1107 | fragheader.header.type = htons(GNUNET_MESSAGE_TYPE_WLAN_FRAGMENT); | 1125 | fragheader.header.type = htons(GNUNET_MESSAGE_TYPE_WLAN_FRAGMENT); |
1108 | 1126 | ||
1109 | 1127 | ||
@@ -1136,7 +1154,7 @@ do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1136 | size += sizeof(struct RadiotapHeader) + sizeof(struct IeeeHeader) | 1154 | size += sizeof(struct RadiotapHeader) + sizeof(struct IeeeHeader) |
1137 | + sizeof(struct GNUNET_MessageHeader); | 1155 | + sizeof(struct GNUNET_MessageHeader); |
1138 | msgheader = GNUNET_malloc(size); | 1156 | msgheader = GNUNET_malloc(size); |
1139 | msgheader->size = htons(size - sizeof(struct GNUNET_MessageHeader)); | 1157 | msgheader->size = htons(size); |
1140 | msgheader->type = htons(GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA); | 1158 | msgheader->type = htons(GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA); |
1141 | 1159 | ||
1142 | radioHeader = (struct RadiotapHeader*) &msgheader[1]; | 1160 | radioHeader = (struct RadiotapHeader*) &msgheader[1]; |
@@ -1286,7 +1304,7 @@ wlan_plugin_send (void *cls, | |||
1286 | (newmsg->msg) = GNUNET_malloc(msgbuf_size + sizeof(struct WlanHeader)); | 1304 | (newmsg->msg) = GNUNET_malloc(msgbuf_size + sizeof(struct WlanHeader)); |
1287 | wlanheader = (struct WlanHeader *) newmsg->msg; | 1305 | wlanheader = (struct WlanHeader *) newmsg->msg; |
1288 | //copy msg to buffer, not fragmented / segmented yet, but with message header | 1306 | //copy msg to buffer, not fragmented / segmented yet, but with message header |
1289 | wlanheader->header.size = htons(msgbuf_size); | 1307 | wlanheader->header.size = htons(msgbuf_size + sizeof(struct WlanHeader)); |
1290 | wlanheader->header.type = htons(GNUNET_MESSAGE_TYPE_WLAN_DATA); | 1308 | wlanheader->header.type = htons(GNUNET_MESSAGE_TYPE_WLAN_DATA); |
1291 | memcpy(&(wlanheader->target), target, sizeof(struct GNUNET_PeerIdentity)); | 1309 | memcpy(&(wlanheader->target), target, sizeof(struct GNUNET_PeerIdentity)); |
1292 | wlanheader->crc = htonl(getcrc32(msgbuf, msgbuf_size)); | 1310 | wlanheader->crc = htonl(getcrc32(msgbuf, msgbuf_size)); |
@@ -1302,6 +1320,7 @@ wlan_plugin_send (void *cls, | |||
1302 | 1320 | ||
1303 | } | 1321 | } |
1304 | 1322 | ||
1323 | //TODO doxigen | ||
1305 | static struct FragmentMessage * | 1324 | static struct FragmentMessage * |
1306 | get_fragment_message_from_session(struct Session * session) | 1325 | get_fragment_message_from_session(struct Session * session) |
1307 | { | 1326 | { |
@@ -1361,6 +1380,8 @@ wlan_plugin_disconnect(void *cls, const struct GNUNET_PeerIdentity *target) | |||
1361 | fm = get_fragment_message_from_session(queue->content); | 1380 | fm = get_fragment_message_from_session(queue->content); |
1362 | free_fragment_message(plugin,fm); | 1381 | free_fragment_message(plugin,fm); |
1363 | 1382 | ||
1383 | //dispose all received fragments | ||
1384 | free_rec_frag_queue(queue->content); | ||
1364 | 1385 | ||
1365 | // remove PendingMessage | 1386 | // remove PendingMessage |
1366 | pm = queue->content->pending_message; | 1387 | pm = queue->content->pending_message; |
@@ -1499,9 +1520,125 @@ wlan_plugin_address_to_string (void *cls, | |||
1499 | return GNUNET_strdup (ret); | 1520 | return GNUNET_strdup (ret); |
1500 | } | 1521 | } |
1501 | 1522 | ||
1523 | /** | ||
1524 | * Function to test if fragment number already exists in the fragments received | ||
1525 | */ | ||
1526 | //TODO doxigen | ||
1527 | static const int | ||
1528 | is_double_msg(struct Session * session, struct FragmentationHeader * fh) | ||
1529 | { | ||
1530 | struct RecQueue * rec_queue = session->frag_head; | ||
1531 | while (rec_queue != NULL) | ||
1532 | { | ||
1533 | if (rec_queue->num == fh->fragment_off_or_num) | ||
1534 | { | ||
1535 | return GNUNET_YES; | ||
1536 | } | ||
1537 | rec_queue = rec_queue->next; | ||
1502 | 1538 | ||
1539 | } | ||
1540 | return GNUNET_NO; | ||
1541 | } | ||
1503 | 1542 | ||
1504 | 1543 | ||
1544 | static void | ||
1545 | insert_fragment_in_queue(struct Session * session, struct RecQueue * rec_queue) | ||
1546 | { | ||
1547 | struct RecQueue * rec_queue2 = session->frag_head; | ||
1548 | struct WlanHeader * wlanheader = NULL; | ||
1549 | //first received fragment of message | ||
1550 | if (session->rec_size == NO_MESSAGE_OR_MESSAGE_FINISHED) | ||
1551 | { | ||
1552 | session->rec_size = MESSAGE_LENGHT_UNKNOWN; | ||
1553 | } | ||
1554 | //this is the first fragment of the message (fragment id 0) | ||
1555 | if (rec_queue->num == 0) | ||
1556 | { | ||
1557 | wlanheader = (struct WlanHeader *) rec_queue->msg; | ||
1558 | session->rec_size = wlanheader->header.size; | ||
1559 | } | ||
1560 | |||
1561 | //sort into list | ||
1562 | while (rec_queue2 != NULL) | ||
1563 | { | ||
1564 | if (rec_queue2->num > rec_queue->num) | ||
1565 | { | ||
1566 | //next element number is grater than the current num | ||
1567 | GNUNET_CONTAINER_DLL_insert_before(session->frag_head, session->frag_tail, rec_queue2, rec_queue); | ||
1568 | return; | ||
1569 | } | ||
1570 | rec_queue = rec_queue->next; | ||
1571 | } | ||
1572 | //no element has a grater number | ||
1573 | GNUNET_CONTAINER_DLL_insert_tail(session->frag_head, session->frag_tail, rec_queue); | ||
1574 | } | ||
1575 | |||
1576 | /** | ||
1577 | * Function to dispose the fragments received for a message | ||
1578 | */ | ||
1579 | //TODO doxigen | ||
1580 | static void | ||
1581 | free_rec_frag_queue(struct Session * session) | ||
1582 | { | ||
1583 | struct RecQueue * rec_queue = session->frag_head; | ||
1584 | struct RecQueue * rec_queue2; | ||
1585 | while (rec_queue != NULL) | ||
1586 | { | ||
1587 | rec_queue2 = rec_queue; | ||
1588 | rec_queue = rec_queue->next; | ||
1589 | GNUNET_free(rec_queue2); | ||
1590 | } | ||
1591 | session->frag_head = NULL; | ||
1592 | session->frag_tail = NULL; | ||
1593 | session->rec_size = NO_MESSAGE_OR_MESSAGE_FINISHED; | ||
1594 | } | ||
1595 | |||
1596 | /** | ||
1597 | * Function to check if all fragments of a message have been received | ||
1598 | */ | ||
1599 | //TODO doxigen | ||
1600 | static void | ||
1601 | check_rec_finished_msg (struct Plugin* plugin, struct Session_light * session_light, struct Session * session){ | ||
1602 | struct RecQueue * rec_queue = session->frag_head; | ||
1603 | int packetsize = session->rec_size; | ||
1604 | int sum = 0; | ||
1605 | int aktnum = 0; | ||
1606 | char * msg; | ||
1607 | //some fragment should be received | ||
1608 | GNUNET_assert(session->rec_size != NO_MESSAGE_OR_MESSAGE_FINISHED); | ||
1609 | //check if first fragment is present | ||
1610 | if (session->rec_size == MESSAGE_LENGHT_UNKNOWN){ | ||
1611 | return; | ||
1612 | } | ||
1613 | while (rec_queue != NULL){ | ||
1614 | sum += rec_queue->size; | ||
1615 | //check if all fragment numbers are present | ||
1616 | if (rec_queue->num != aktnum){ | ||
1617 | return; | ||
1618 | } | ||
1619 | aktnum ++; | ||
1620 | rec_queue = rec_queue->next; | ||
1621 | } | ||
1622 | //sum should always be smaller or equal of | ||
1623 | GNUNET_assert(sum <= packetsize); | ||
1624 | if (sum == packetsize){ | ||
1625 | //copy fragments together | ||
1626 | msg = GNUNET_malloc(packetsize); | ||
1627 | rec_queue = session->frag_head; | ||
1628 | aktnum = 0; | ||
1629 | while (rec_queue != NULL){ | ||
1630 | memcpy(msg + aktnum, rec_queue->msg, rec_queue->size); | ||
1631 | aktnum += rec_queue->size; | ||
1632 | rec_queue = rec_queue->next; | ||
1633 | } | ||
1634 | free_rec_frag_queue(session); | ||
1635 | //call wlan_process_helper to process the message | ||
1636 | wlan_process_helper (plugin, session_light, (struct GNUNET_MessageHeader*) msg); | ||
1637 | |||
1638 | GNUNET_free(msg); | ||
1639 | } | ||
1640 | } | ||
1641 | |||
1505 | /** | 1642 | /** |
1506 | * Function used for to process the data from the suid process | 1643 | * Function used for to process the data from the suid process |
1507 | */ | 1644 | */ |
@@ -1518,6 +1655,7 @@ wlan_process_helper (void *cls, | |||
1518 | struct WlanHeader * wlanheader = NULL; | 1655 | struct WlanHeader * wlanheader = NULL; |
1519 | struct FragmentationHeader * fh = NULL; | 1656 | struct FragmentationHeader * fh = NULL; |
1520 | struct FragmentMessage * fm = NULL; | 1657 | struct FragmentMessage * fm = NULL; |
1658 | struct RecQueue * rec_queue = NULL; | ||
1521 | const struct GNUNET_MessageHeader * temp_hdr = NULL; | 1659 | const struct GNUNET_MessageHeader * temp_hdr = NULL; |
1522 | const char * tempmsg = NULL; | 1660 | const char * tempmsg = NULL; |
1523 | struct Session_light * session_light; | 1661 | struct Session_light * session_light; |
@@ -1626,7 +1764,7 @@ wlan_process_helper (void *cls, | |||
1626 | } | 1764 | } |
1627 | session = session_light->session; | 1765 | session = session_light->session; |
1628 | 1766 | ||
1629 | fh = (struct FragmentationHeader *) &hdr[1]; | 1767 | fh = (struct FragmentationHeader *) hdr; |
1630 | tempmsg = (char*) &fh[1]; | 1768 | tempmsg = (char*) &fh[1]; |
1631 | 1769 | ||
1632 | //if not in session list | 1770 | //if not in session list |
@@ -1641,7 +1779,38 @@ wlan_process_helper (void *cls, | |||
1641 | } | 1779 | } |
1642 | else | 1780 | else |
1643 | { | 1781 | { |
1644 | //todo fragments to message | 1782 | //todo fragments do not timeout |
1783 | //check if message_id is rigth or it is a new msg | ||
1784 | if ((session->message_id_in == ntohs(fh->message_id)) | ||
1785 | || (session->rec_size == NO_MESSAGE_OR_MESSAGE_FINISHED)) | ||
1786 | { | ||
1787 | session->message_id_in = ntohs(fh->message_id); | ||
1788 | if (is_double_msg(session, fh) != GNUNET_YES) | ||
1789 | { | ||
1790 | rec_queue = GNUNET_malloc(sizeof (struct RecQueue) + | ||
1791 | ntohs(fh->header.size) - sizeof(struct FragmentationHeader)); | ||
1792 | rec_queue->size = ntohs(fh->header.size | ||
1793 | - sizeof(struct FragmentationHeader)); | ||
1794 | rec_queue->num = ntohs(fh->fragment_off_or_num); | ||
1795 | rec_queue->msg = (char*) &rec_queue[1]; | ||
1796 | //copy msg to buffer | ||
1797 | memcpy((char*) rec_queue->msg, tempmsg, rec_queue->size); | ||
1798 | insert_fragment_in_queue(session, rec_queue); | ||
1799 | check_rec_finished_msg(plugin, session_light, session); | ||
1800 | } | ||
1801 | else | ||
1802 | { | ||
1803 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, | ||
1804 | "WLAN fragment is a clone\n"); | ||
1805 | return; | ||
1806 | } | ||
1807 | } | ||
1808 | else | ||
1809 | { | ||
1810 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, | ||
1811 | "WLAN fragment message_id and session message_id are not the same and a message is already (partly) received\n"); | ||
1812 | return; | ||
1813 | } | ||
1645 | } | 1814 | } |
1646 | } | 1815 | } |
1647 | else | 1816 | else |
@@ -1705,7 +1874,7 @@ wlan_process_helper (void *cls, | |||
1705 | 1874 | ||
1706 | else if (ntohs(hdr->type) == GNUNET_MESSAGE_TYPE_WLAN_HELPER_CONTROL) | 1875 | else if (ntohs(hdr->type) == GNUNET_MESSAGE_TYPE_WLAN_HELPER_CONTROL) |
1707 | { | 1876 | { |
1708 | //TODO Control | 1877 | //TODO more control |
1709 | if (ntohs(hdr->size) == 6) | 1878 | if (ntohs(hdr->size) == 6) |
1710 | { | 1879 | { |
1711 | plugin->mac_address = GNUNET_malloc(6); | 1880 | plugin->mac_address = GNUNET_malloc(6); |
@@ -1730,6 +1899,8 @@ wlan_process_helper (void *cls, | |||
1730 | else | 1899 | else |
1731 | { | 1900 | { |
1732 | // TODO Wrong data? | 1901 | // TODO Wrong data? |
1902 | GNUNET_log(GNUNET_ERROR_TYPE_INFO, "WLAN packet has not the right type\n"); | ||
1903 | return; | ||
1733 | } | 1904 | } |
1734 | } | 1905 | } |
1735 | 1906 | ||
@@ -1879,17 +2050,17 @@ libgnunet_plugin_transport_wlan_init (void *cls) | |||
1879 | * Exit point from the plugin. | 2050 | * Exit point from the plugin. |
1880 | */ | 2051 | */ |
1881 | //TODO doxigen | 2052 | //TODO doxigen |
1882 | //fixme cleanup | 2053 | //FIXME cleanup |
1883 | void * | 2054 | void * |
1884 | libgnunet_plugin_transport_wlan_done (void *cls) | 2055 | libgnunet_plugin_transport_wlan_done (void *cls) |
1885 | { | 2056 | { |
1886 | struct GNUNET_TRANSPORT_PluginFunctions *api = cls; | 2057 | struct GNUNET_TRANSPORT_PluginFunctions *api = cls; |
1887 | struct Plugin *plugin = api->cls; | 2058 | struct Plugin *plugin = api->cls; |
1888 | 2059 | ||
1889 | GNUNET_SERVER_mst_destroy(plugin->consoltoken); | ||
1890 | |||
1891 | GNUNET_assert(cls !=NULL); | 2060 | GNUNET_assert(cls !=NULL); |
1892 | 2061 | ||
2062 | GNUNET_SERVER_mst_destroy(plugin->consoltoken); | ||
2063 | |||
1893 | GNUNET_free_non_null(plugin->mac_address); | 2064 | GNUNET_free_non_null(plugin->mac_address); |
1894 | GNUNET_free (plugin); | 2065 | GNUNET_free (plugin); |
1895 | GNUNET_free (api); | 2066 | GNUNET_free (api); |