Sebastián Pastor / EtheriosCloudConnector
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers connector_cc.h Source File

connector_cc.h

00001 /*
00002  * Copyright (c) 2013 Digi International Inc.,
00003  * All rights not expressly granted are reserved.
00004  *
00005  * This Source Code Form is subject to the terms of the Mozilla Public
00006  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
00007  * You can obtain one at http://mozilla.org/MPL/2.0/.
00008  *
00009  * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
00010  * =======================================================================
00011  */
00012 
00013 #define FAC_CC_DISCONNECT           0x00
00014 #define FAC_CC_REDIRECT_TO_SDA      0x03
00015 #define FAC_CC_REDIRECT_REPORT      0x04
00016 #define FAC_CC_CONNECTION_REPORT    0x05
00017 #define FAC_CC_REBOOT               0x06
00018 
00019 #define CC_REDIRECT_URL_COUNT    2
00020 
00021 typedef enum {
00022     cc_not_redirect,
00023     cc_redirect_success,
00024     cc_redirect_error
00025 } cc_redirect_status_t;
00026 
00027 typedef enum {
00028     ethernet_type = 1,
00029     ppp_over_modem_type
00030 } cc_connection_type_t;
00031 
00032 enum {
00033     cc_state_redirect_report,
00034     cc_state_connect_report,
00035     cc_state_close,
00036     cc_state_connect
00037 
00038 };
00039 
00040 
00041 typedef struct {
00042     char    origin_url[CLOUD_URL_LENGTH];
00043     size_t  origin_url_length;
00044 
00045     uint8_t * redirect;
00046 
00047     int     state;
00048     size_t redirect_count;
00049     uint8_t report_code;
00050 } connector_cc_data_t;
00051 
00052 static void cc_init(connector_cc_data_t * const cc_ptr)
00053 {
00054     cc_ptr->state = cc_state_redirect_report;
00055     cc_ptr->report_code = cc_not_redirect;
00056     cc_ptr->origin_url_length = 0;
00057 
00058 }
00059 
00060 static connector_status_t edp_send_redirect_report(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr)
00061 {
00062 
00063 enum cc_redirect_report {
00064     field_define(redirect_report, opcode, uint8_t),
00065     field_define(redirect_report, code, uint8_t),
00066     field_define(redirect_report, message_length, uint8_t),
00067     /* TODO: Need to fix this enum if message_length > 0. For now, cloud always send message length =  0 */
00068     field_define(redirect_report, url_length, uint16_t),
00069     record_end(redirect_report)
00070 };
00071 
00072     size_t const report_message_length = 0;
00073     size_t const redirect_report_header_size = record_bytes(redirect_report);
00074 
00075     connector_status_t result = connector_working;
00076     uint8_t * edp_header;
00077     uint8_t * redirect_report;
00078     size_t url_length;
00079     size_t redirect_length;
00080 
00081     ASSERT(report_message_length == 0);
00082 
00083     connector_debug_printf("Connection Control: send redirect_report\n");
00084 
00085     /* build and send redirect report
00086      *  ----------------------------------------------------
00087      * |   0    |    1   |    2    |   3...  | x1-x2  | ... |
00088      *  ----------------------------------------------------
00089      * | opcode | report | message | report  |  url   | url |
00090      * |        |  code  | length  | message | length |     |
00091      *  ----------------------------------------------------
00092      */
00093 
00094     edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_CC_NUM, &redirect_report, &redirect_length);
00095     if (edp_header == NULL)
00096     {
00097         result = connector_pending;
00098         goto done;
00099     }
00100 
00101     message_store_u8(redirect_report, opcode, FAC_CC_REDIRECT_REPORT);
00102     message_store_u8(redirect_report, code, cc_ptr->report_code);
00103     {
00104         uint8_t const length8 = (uint8_t) report_message_length;
00105 
00106         ASSERT(report_message_length <= UCHAR_MAX);
00107         message_store_u8(redirect_report, message_length, length8);
00108     }
00109 
00110     url_length = cc_ptr->origin_url_length;
00111 
00112     if (url_length > 0)
00113     {
00114         char const prefix_url[] = URL_PREFIX;
00115         size_t const prefix_len = sizeof prefix_url -1;
00116         uint8_t * const redirect_report_url = redirect_report + redirect_report_header_size;
00117 
00118         ASSERT(redirect_length > (redirect_report_header_size + prefix_len + cc_ptr->origin_url_length));
00119 
00120         memcpy(redirect_report_url, prefix_url, prefix_len);
00121         memcpy((redirect_report_url + prefix_len), cc_ptr->origin_url, cc_ptr->origin_url_length);
00122 
00123         url_length += prefix_len;
00124 
00125     }
00126     {
00127         uint16_t const length16 = (uint16_t) url_length;
00128 
00129         ASSERT(url_length <= UINT16_MAX);
00130         message_store_be16(redirect_report, url_length, length16);
00131     }
00132 
00133     result = tcp_initiate_send_facility_packet(connector_ptr, edp_header,
00134                                            redirect_report_header_size + url_length,
00135                                            E_MSG_FAC_CC_NUM, tcp_release_packet_buffer, NULL);
00136 done:
00137     return result;
00138 }
00139 
00140 static void build_ip_addr(connector_data_t * const connector_ptr, uint8_t * ipv6_addr)
00141 {
00142     connector_debug_hexvalue("Send device IP address", connector_ptr->edp_data.config.ip_addr, (int) connector_ptr->edp_data.config.ip_addr_length);
00143 
00144     if (connector_ptr->edp_data.config.ip_addr_length == CC_IPV6_ADDRESS_LENGTH)
00145     {
00146         /* Just copy IPv6 address */
00147         memcpy(ipv6_addr, connector_ptr->edp_data.config.ip_addr, CC_IPV6_ADDRESS_LENGTH);
00148     }
00149     else
00150     {
00151         /* good ipv4 addr. Map ipv4 to ipv6 address:
00152          * 10 all-zeros octets, 2 all-ones octets, and
00153          * then the ipv4 addr
00154          */
00155 
00156         static unsigned char const ipv6_padding_for_ipv4[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
00157         size_t const padding_length = sizeof ipv6_padding_for_ipv4/sizeof ipv6_padding_for_ipv4[0];
00158 
00159         ASSERT(padding_length == (CC_IPV6_ADDRESS_LENGTH - CC_IPV4_ADDRESS_LENGTH));
00160 
00161         memcpy(ipv6_addr, ipv6_padding_for_ipv4, padding_length);
00162         ipv6_addr += padding_length;
00163         memcpy(ipv6_addr, connector_ptr->edp_data.config.ip_addr, CC_IPV4_ADDRESS_LENGTH);
00164     }
00165 
00166     return;
00167 
00168 }
00169 
00170 static connector_status_t send_connection_report(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr)
00171 {
00172 #define FAC_CC_CLIENTTYPE_REBOOTABLE_DEVICE 2
00173 
00174 enum cc_connection_report {
00175     field_define(connection_report, opcode, uint8_t),
00176     field_define(connection_report, client_type, uint8_t),
00177     field_define(connection_report, ip1, uint32_t),
00178     field_define(connection_report, ip2, uint32_t),
00179     field_define(connection_report, ip3, uint32_t),
00180     field_define(connection_report, ip4, uint32_t),
00181     field_define(connection_report, connection_type, uint8_t),
00182     record_end(connection_report)
00183 };
00184 
00185 enum cc_connection_info {
00186     field_define(connection_info, link_speed, uint32_t),
00187     record_end(connection_info)
00188 };
00189     connector_status_t result = connector_working;
00190     uint8_t * edp_header;
00191     uint8_t * connection_report;
00192     size_t avail_length;
00193     size_t report_length;
00194 
00195     UNUSED_PARAMETER(cc_ptr);
00196 
00197     connector_debug_printf("Connection Control: send connection report\n");
00198 
00199     /* Build Connection report
00200      *  -------------------------------------------------------
00201      * |   0    |    1   | 2  - 17 |     18      |  19 ...     |
00202      *  -------------------------------------------------------
00203      * | opcode | client |   IP    | connection  | connection  |
00204      * |        |  type  | address |    type     | information |
00205      *  -------------------------------------------------------
00206      *
00207      * 1. call callback to get ip address & build ip_address
00208      * 2. call callback to get & build connection type
00209      * 3. if connection type is LAN, call callback to get and build mac address for connection information
00210      * 4. if connection type is WAN, call callback to get and build link speed for connection information
00211      * 5. if connection type is WAN, call callback to get and build phone number for connection information
00212      */
00213     edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_CC_NUM, &connection_report, &avail_length);
00214     if (edp_header == NULL)
00215     {
00216         result = connector_pending;
00217         goto done;
00218     }
00219 
00220     {
00221         result = get_config_ip_addr(connector_ptr);
00222 
00223         if (result != connector_working)
00224         {
00225             goto done;
00226         }
00227 
00228         message_store_u8(connection_report, opcode, FAC_CC_CONNECTION_REPORT);
00229         message_store_u8(connection_report, client_type, FAC_CC_CLIENTTYPE_REBOOTABLE_DEVICE);
00230         report_length = field_named_data(connection_report, opcode, size)+field_named_data(connection_report, client_type, size);
00231 
00232         build_ip_addr(connector_ptr, (connection_report+report_length));
00233         report_length += CC_IPV6_ADDRESS_LENGTH;
00234 
00235     }
00236 
00237     {
00238         cc_connection_type_t const connection_type =  (connector_ptr->connection_type == connector_connection_type_lan) ? ethernet_type : ppp_over_modem_type;
00239 
00240         message_store_u8(connection_report, connection_type, connection_type);
00241 
00242         report_length += field_named_data(connection_report, connection_type, size);
00243 
00244         switch (connector_ptr->connection_type)
00245         {
00246         case connector_connection_type_lan:
00247             result = get_config_mac_addr(connector_ptr);
00248             if (result != connector_working)
00249             {
00250                 goto done;
00251 
00252             }
00253 
00254             {
00255                 uint8_t * report_ptr = connection_report + report_length;
00256 
00257                 ASSERT(connector_ptr->mac_addr != NULL);
00258                 /* build MAC address for LAN connection type */
00259                 memcpy(report_ptr, connector_ptr->mac_addr, MAC_ADDR_LENGTH);
00260                 report_length += MAC_ADDR_LENGTH;
00261 
00262                 connector_debug_hexvalue("Sending MAC address", connector_ptr->mac_addr, MAC_ADDR_LENGTH);
00263 
00264                 break;
00265             }
00266         case connector_connection_type_wan:
00267             {
00268                 /* build Link speed for WAN connection type */
00269                 uint8_t * connection_info = connection_report + record_bytes(connection_report);
00270 
00271 
00272 #if (defined CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND)
00273                 message_store_be32(connection_info, link_speed, CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND);
00274                 connector_debug_printf("send_connection_report: link_speed = %d\n", CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND);
00275 
00276 #else
00277                 message_store_be32(connection_info, link_speed, connector_ptr->link_speed);
00278                 connector_debug_printf("send_connection_report: link_speed = %d\n", connector_ptr->link_speed);
00279 #endif
00280                 report_length += field_named_data(connection_info, link_speed, size);
00281 
00282                 {
00283                     /* build phone number for WAN connection type */
00284 
00285 #if (defined CONNECTOR_WAN_PHONE_NUMBER_DIALED)
00286                     char const phone[] = CONNECTOR_WAN_PHONE_NUMBER_DIALED;
00287                     size_t const length = sizeof phone -1;
00288 #else
00289                     size_t length = connector_ptr->phone_dialed_length;
00290                     char * phone = connector_ptr->phone_dialed;
00291 #endif
00292                     connector_debug_printf("send_connection_report: phone number = %.*s\n", (int)length, phone);
00293                     memcpy(connection_report+report_length, phone, length);
00294                     report_length += length;
00295                     *(connection_report + report_length) = 0x0;
00296                     report_length++;
00297                 }
00298             }
00299         }
00300     }
00301 
00302     ASSERT(avail_length > report_length);
00303     result = tcp_initiate_send_facility_packet(connector_ptr, edp_header, report_length, E_MSG_FAC_CC_NUM, tcp_release_packet_buffer, NULL);
00304 
00305 done:
00306     return result;
00307 }
00308 
00309 
00310 static connector_status_t  process_redirect(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr, uint8_t const * const packet)
00311 {
00312 enum cc_redirect {
00313     field_define(redirect, opcode, uint8_t),
00314     field_define(redirect, count, uint8_t),
00315     record_end(redirect)
00316 };
00317 
00318 
00319     connector_status_t result = connector_working;
00320     uint8_t * redirect;
00321 
00322     connector_debug_printf("process_redirect:  redirect to new destination\n");
00323     /* Redirect to new destination:
00324      * The connector will close connection and connect to new destination. If connect
00325      * to new destination fails, this function will returns SUCCESS to try
00326      * connecting to the original destination.
00327      */
00328 
00329     /*
00330      * parse new destinations
00331      *  --------------------------------------------------------------------
00332      * |   0    |     1     |    2 - 3    |  4 ... |              |         |
00333      *  --------------------------------------------------------------------
00334      * | opcode | URL count | URL 1 Length|  URL 1 | URL 2 Length |  URL 2  |
00335      *  --------------------------------------------------------------------
00336      *
00337     */
00338     redirect = GET_PACKET_DATA_POINTER(packet, PACKET_EDP_FACILITY_SIZE);
00339     cc_ptr->redirect_count = message_load_u8(redirect, count);
00340 
00341     if (cc_ptr->redirect_count == 0)
00342     {   /* nothing to redirect */
00343         connector_debug_printf("cc_process_redirect: redirect with no url specified\n");
00344         result = connector_idle;
00345         goto done;
00346     }
00347 
00348     if (cc_ptr->redirect_count > CC_REDIRECT_URL_COUNT)
00349     {
00350         cc_ptr->redirect_count = CC_REDIRECT_URL_COUNT;
00351     }
00352 
00353 
00354     /* let's start parsing url length and url string */
00355     redirect += record_bytes(redirect);
00356     cc_ptr->redirect = redirect;
00357 
00358     /* save original Device Cloud url that we connected before */
00359     memcpy(cc_ptr->origin_url, connector_ptr->edp_data.config.cloud_url, connector_ptr->edp_data.config.cloud_url_length);
00360     cc_ptr->origin_url_length = connector_ptr->edp_data.config.cloud_url_length;
00361 
00362     /* Close the connection before parsing new destination url */
00363     edp_set_active_state(connector_ptr, connector_transport_redirect);
00364     cc_ptr->state = cc_state_close;
00365 
00366 done:
00367     return result;
00368 }
00369 
00370 static connector_status_t cc_process(connector_data_t * const connector_ptr, void * const facility_data,
00371                                           uint8_t * const packet, unsigned int * const receive_timeout)
00372 {
00373     connector_status_t result = connector_idle;
00374 
00375     UNUSED_PARAMETER(receive_timeout);
00376 
00377     /* process incoming message from Device Cloud for Connection Control facility */
00378     if (packet != NULL)
00379     {
00380         uint8_t opcode;
00381         uint8_t * ptr;
00382         connector_cc_data_t * cc_ptr = facility_data;
00383 
00384         /* get the DATA portion of the packet */
00385         ptr = GET_PACKET_DATA_POINTER(packet, PACKET_EDP_FACILITY_SIZE);
00386         opcode = *ptr;
00387 
00388         switch (opcode)
00389         {
00390 
00391         case FAC_CC_DISCONNECT:
00392         {
00393 
00394             edp_set_close_status(connector_ptr, connector_close_status_cloud_disconnected);
00395             edp_set_active_state(connector_ptr, connector_transport_close);
00396             result = connector_working;
00397             break;
00398         }
00399         case FAC_CC_REDIRECT_TO_SDA:
00400             result = process_redirect(connector_ptr, cc_ptr, packet);
00401             break;
00402         case FAC_CC_REBOOT:
00403             result = connector_reboot(connector_ptr);
00404             break;
00405         default:
00406             connector_debug_printf("connector_cc_process: unsupported opcode %u\n", opcode);
00407             break;
00408         }
00409     }
00410 
00411     return result;
00412 }
00413 static connector_status_t cc_discovery(connector_data_t * const connector_ptr, void * const facility_data,
00414                                             uint8_t * const packet, unsigned int * receive_timeout)
00415 {
00416     connector_status_t result = connector_idle;
00417     connector_cc_data_t * cc_ptr = facility_data;
00418 
00419     UNUSED_PARAMETER(receive_timeout);
00420     UNUSED_PARAMETER(packet);
00421     /* Connection control facility needs to send redirect and
00422      * connection reports on discovery layer.
00423      */
00424     if (cc_ptr->state == cc_state_redirect_report)
00425     {
00426         result = edp_send_redirect_report(connector_ptr, cc_ptr);
00427         if (result == connector_working)
00428         {
00429             result = connector_pending; /* still not complete cc_discovery */
00430             cc_ptr->report_code = cc_not_redirect;
00431             cc_ptr->origin_url_length = 0;
00432             cc_ptr->state = cc_state_connect_report;
00433         }
00434     }
00435     else if (cc_ptr->state == cc_state_connect_report)
00436     {
00437         result = send_connection_report(connector_ptr, cc_ptr);
00438     }
00439 
00440     return result;
00441 }
00442 
00443 static connector_status_t connector_facility_cc_cleanup(connector_data_t * const connector_ptr)
00444 {
00445     connector_status_t result = connector_working;
00446     connector_cc_data_t * const cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM);
00447     if (cc_ptr != NULL && cc_ptr->state != cc_state_close)
00448     {
00449         cc_ptr->state = cc_state_redirect_report;
00450         cc_ptr->report_code = cc_not_redirect;
00451         cc_ptr->origin_url_length = 0;
00452         cc_ptr->redirect_count = 0;
00453     }
00454 
00455     return result;
00456 }
00457 
00458 static connector_status_t connector_facility_cc_delete(connector_data_t * const connector_ptr)
00459 {
00460     return del_facility_data(connector_ptr, E_MSG_FAC_CC_NUM);
00461 }
00462 
00463 static connector_status_t connector_facility_cc_init(connector_data_t * const connector_ptr, unsigned int const facility_index)
00464 {
00465     connector_status_t result = connector_working;
00466     connector_cc_data_t * cc_ptr;
00467 
00468     /* Add Connection control facility
00469      *
00470      * Make sure connection control is not already created. If Connection
00471      * control facility is already created, we probably reconnect to Device Cloud
00472      * so just need to reset to initial state.
00473      *
00474      */
00475     cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM);
00476     if (cc_ptr == NULL)
00477     {
00478         void * ptr;
00479 
00480         result = add_facility_data(connector_ptr, facility_index, E_MSG_FAC_CC_NUM, &ptr, sizeof *cc_ptr);
00481 
00482         if (result != connector_working || ptr == NULL)
00483         {
00484             goto done;
00485         }
00486         cc_ptr = ptr;
00487 
00488         cc_init(cc_ptr);
00489     }
00490 
00491 done:
00492     return result;
00493 }
00494 
00495 static connector_status_t edp_redirect_process(connector_data_t * const connector_ptr)
00496 {
00497 
00498     connector_status_t result = connector_working;
00499 
00500 
00501     connector_cc_data_t * const cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM);
00502     ASSERT(cc_ptr != NULL);
00503 
00504     switch (cc_ptr->state)
00505     {
00506     case cc_state_close:
00507 
00508         /* set the reason for closing */
00509         edp_set_close_status(connector_ptr, connector_close_status_cloud_redirected);
00510 
00511         result = tcp_close_cloud(connector_ptr);
00512         if (result == connector_working)
00513         {
00514             if (edp_get_active_state(connector_ptr) == connector_transport_idle)
00515             {
00516                 /* continue to redirect state */
00517                 edp_set_active_state(connector_ptr, connector_transport_redirect);
00518                 cc_ptr->state = cc_state_connect;
00519                 goto done;
00520             }
00521         }
00522 
00523         if (result != connector_pending && result != connector_abort && result != connector_device_terminated)
00524         {
00525             /* reset initial value */
00526             cc_init(cc_ptr);
00527         }
00528         break;
00529 
00530     case cc_state_connect:
00531 
00532         /* let parse url length and url string */
00533         if (cc_ptr->redirect_count > 0)
00534         {
00535             enum cc_redirect_url {
00536                 field_define(redirect, url_length, uint16_t),
00537                 record_end(redirect_url_length)
00538             };
00539 
00540             uint8_t * redirect = cc_ptr->redirect;
00541             char const * cloud_url;
00542             uint16_t cloud_url_length;
00543 
00544             uint16_t const url_length = message_load_be16(redirect, url_length);
00545             redirect += record_bytes(redirect_url_length);
00546 
00547             {
00548                 size_t const prefix_len = sizeof URL_PREFIX -1;
00549 
00550                 cloud_url = (char const *)redirect;
00551                 cloud_url_length = url_length;
00552 
00553                 if (memcmp(cloud_url, URL_PREFIX, prefix_len) == 0)
00554                 {
00555                     cloud_url += prefix_len;
00556                     cloud_url_length -= (uint16_t) prefix_len;
00557                 }
00558 
00559                 ASSERT(cloud_url_length < sizeof connector_ptr->edp_data.config.cloud_url);
00560                 ASSERT(cloud_url_length != 0);
00561 
00562                 memcpy(connector_ptr->edp_data.config.cloud_url, cloud_url, cloud_url_length);
00563                 connector_ptr->edp_data.config.cloud_url_length = cloud_url_length;
00564                 connector_ptr->edp_data.config.cloud_url[cloud_url_length] = 0x0;
00565 
00566                 result = connect_to_cloud(connector_ptr, connector_ptr->edp_data.config.cloud_url);
00567             }
00568 
00569             if (result == connector_working)
00570             {
00571                 cc_ptr->report_code = cc_redirect_success;
00572                 cc_ptr->state = cc_state_redirect_report;
00573                 edp_set_active_state(connector_ptr, connector_transport_open);
00574                 edp_set_edp_state(connector_ptr, edp_communication_send_version);
00575 
00576                 connector_ptr->edp_data.send_packet.packet_buffer.in_use = connector_false;
00577                 connector_ptr->edp_data.receive_packet.packet_buffer.in_use = connector_false;
00578                 connector_ptr->edp_data.receive_packet.packet_buffer.next = NULL;
00579                 connector_ptr->edp_data.receive_packet.free_packet_buffer = &connector_ptr->edp_data.receive_packet.packet_buffer;
00580             }
00581             else if (result != connector_pending && result != connector_abort)
00582             {
00583                 /* connect to next redirect url */
00584                 result = connector_pending;
00585                 cc_ptr->redirect = redirect;
00586                 cc_ptr->redirect += url_length;
00587                 cc_ptr->redirect_count--;
00588             }
00589         }
00590         else
00591         {
00592             cc_ptr->report_code = cc_redirect_error;
00593             cc_ptr->state = cc_state_redirect_report;
00594             edp_set_active_state(connector_ptr, connector_transport_open);
00595             /* restore the original url */
00596             edp_get_device_cloud(connector_ptr);
00597         }
00598         break;
00599     }
00600 
00601 done:
00602     return result;
00603 }
00604