static void update_battery_level(lwm2m_context_t * context) { static time_t next_change_time = 0; time_t tv_sec; tv_sec = lwm2m_gettime(); if (tv_sec < 0) return; if (next_change_time < tv_sec) { char value[15]; int valueLength; lwm2m_uri_t uri; int level = rand() % 100; if (0 > level) level = -level; if (lwm2m_stringToUri("/3/0/9", 6, &uri)) { valueLength = sprintf(value, "%d", level); fprintf(stderr, "New Battery Level: %d\n", level); handle_value_changed(context, &uri, value, valueLength); } level = rand() % 20; if (0 > level) level = -level; next_change_time = tv_sec + level + 10; } }
static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP, void * message) { coap_packet_t * packet = (coap_packet_t *)message; lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->peerP); if (targetP->status == STATE_REG_PENDING) { time_t tv_sec = lwm2m_gettime(); if (tv_sec >= 0) { targetP->registration = tv_sec; } if (packet != NULL && packet->code == COAP_201_CREATED) { targetP->status = STATE_REGISTERED; if (NULL != targetP->location) { lwm2m_free(targetP->location); } targetP->location = coap_get_multi_option_as_string(packet->location_path); LOG(" => REGISTERED\r\n"); } else { targetP->status = STATE_REG_FAILED; LOG(" => Registration FAILED\r\n"); } } }
static void prv_handleRegistrationUpdateReply(lwm2m_transaction_t * transacP, void * message) { coap_packet_t * packet = (coap_packet_t *)message; lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->peerP); if (targetP->status == STATE_REG_UPDATE_PENDING) { time_t tv_sec = lwm2m_gettime(); if (tv_sec >= 0) { targetP->registration = tv_sec; } if (packet != NULL && packet->code == COAP_204_CHANGED) { targetP->status = STATE_REGISTERED; LOG("Registration update successful"); } else { targetP->status = STATE_REG_FAILED; LOG("Registration update failed"); } } }
static uint8_t prv_checkServerStatus(lwm2m_server_t * serverP) { LOG_ARG("Initial status: %s", STR_STATUS(serverP->status)); switch (serverP->status) { case STATE_BS_HOLD_OFF: serverP->status = STATE_BS_PENDING; LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); break; case STATE_BS_INITIATED: // The ACK was probably lost serverP->status = STATE_BS_PENDING; LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); break; case STATE_DEREGISTERED: // server initiated bootstrap case STATE_BS_PENDING: serverP->registration = lwm2m_gettime() + COAP_EXCHANGE_LIFETIME; break; case STATE_BS_FINISHED: case STATE_BS_FINISHING: case STATE_BS_FAILING: case STATE_BS_FAILED: default: LOG("Returning COAP_IGNORE"); return COAP_IGNORE; } return COAP_NO_ERROR; }
static void prv_handleRegistrationUpdateReply(lwm2m_transaction_t * transacP, void * message) { coap_packet_t * packet = (coap_packet_t *)message; lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->peerP); switch(targetP->status) { case STATE_REG_UPDATE_PENDING: { time_t tv_sec = lwm2m_gettime(); if (tv_sec >= 0) { targetP->registration = tv_sec; } if (packet != NULL && packet->code == CHANGED_2_04) { targetP->status = STATE_REGISTERED; LOG(" => REGISTERED\r\n"); } else { targetP->status = STATE_REG_FAILED; LOG(" => Registration update FAILED\r\n"); } } break; default: break; } }
coap_status_t observe_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, int size, lwm2m_data_t * dataP, coap_packet_t * message, coap_packet_t * response) { lwm2m_watcher_t * watcherP; uint32_t count; LOG("observe_handleRequest()\r\n"); coap_get_header_observe(message, &count); switch (count) { case 0: if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; if (message->token_len == 0) return COAP_400_BAD_REQUEST; watcherP = prv_getWatcher(contextP, uriP, serverP); if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; watcherP->tokenLen = message->token_len; memcpy(watcherP->token, message->token, message->token_len); watcherP->active = true; watcherP->lastTime = lwm2m_gettime(); if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { switch (dataP->type) { case LWM2M_TYPE_INTEGER: if (1 != lwm2m_data_decode_int(dataP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR; break; case LWM2M_TYPE_FLOAT: if (1 != lwm2m_data_decode_float(dataP, &(watcherP->lastValue.asFloat))) return COAP_500_INTERNAL_SERVER_ERROR; break; default: break; } } coap_set_header_observe(response, watcherP->counter++); return COAP_205_CONTENT; case 1: // cancellation observe_cancel(contextP, LWM2M_MAX_ID, serverP->sessionH); return COAP_205_CONTENT; default: return COAP_400_BAD_REQUEST; } }
static void prv_handleResponse(lwm2m_server_t * bootstrapServer, coap_packet_t * message) { if (COAP_204_CHANGED == message->code) { LOG("Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server..."); bootstrapServer->status = STATE_BS_PENDING; bootstrapServer->registration = lwm2m_gettime() + COAP_EXCHANGE_LIFETIME; } else { bootstrapServer->status = STATE_BS_FAILING; } }
lwm2m_context_t * lwm2m_init(void * userData) { lwm2m_context_t * contextP; LOG("Entering"); contextP = (lwm2m_context_t *)lwm2m_malloc(sizeof(lwm2m_context_t)); if (NULL != contextP) { memset(contextP, 0, sizeof(lwm2m_context_t)); contextP->userData = userData; srand((int)lwm2m_gettime()); contextP->nextMID = rand(); } return contextP; }
int send_data(dtls_connection_t *connP, uint8_t * buffer, size_t length) { int nbSent; size_t offset; #ifdef WITH_LOGS char s[INET6_ADDRSTRLEN]; in_port_t port; s[0] = 0; if (AF_INET == connP->addr.sin6_family) { struct sockaddr_in *saddr = (struct sockaddr_in *)&connP->addr; inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN); port = saddr->sin_port; } else if (AF_INET6 == connP->addr.sin6_family) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&connP->addr; inet_ntop(saddr->sin6_family, &saddr->sin6_addr, s, INET6_ADDRSTRLEN); port = saddr->sin6_port; } fprintf(stderr, "Sending %d bytes to [%s]:%hu\r\n", length, s, ntohs(port)); output_buffer(stderr, buffer, length, 0); #endif offset = 0; while (offset != length) { nbSent = sendto(connP->sock, buffer + offset, length - offset, 0, (struct sockaddr *)&(connP->addr), connP->addrLen); if (nbSent == -1) return -1; offset += nbSent; } connP->lastSend = lwm2m_gettime(); return 0; }
dtls_connection_t * connection_new_incoming(dtls_connection_t * connList, int sock, const struct sockaddr * addr, size_t addrLen) { dtls_connection_t * connP; connP = (dtls_connection_t *)malloc(sizeof(dtls_connection_t)); if (connP != NULL) { connP->sock = sock; memcpy(&(connP->addr), addr, addrLen); connP->addrLen = addrLen; connP->next = connList; connP->dtlsSession = (session_t *)malloc(sizeof(session_t)); connP->dtlsSession->addr.sin6 = connP->addr; connP->dtlsSession->size = connP->addrLen; connP->lastSend = lwm2m_gettime(); } return connP; }
int connection_send(dtls_connection_t *connP, uint8_t * buffer, size_t length, coap_protocol_t proto){ if (connP->dtlsSession == NULL) { // no security if ( 0 != send_data(connP, buffer, length)) { return -1 ; } } else { if (DTLS_NAT_TIMEOUT > 0 && (lwm2m_gettime() - connP->lastSend) > DTLS_NAT_TIMEOUT) { // we need to rehandhake because our source IP/port probably changed for the server if ( connection_rehandshake(connP, false) != 0 ) { printf("can't send due to rehandshake error\n"); return -1; } } if (-1 == dtls_write(connP->dtlsContext, connP->dtlsSession, buffer, length)) { return -1; } } return 0; }
lwm2m_transaction_t * transaction_new(void * sessionH, coap_method_t method, char * altPath, lwm2m_uri_t * uriP, uint16_t mID, uint8_t token_len, uint8_t* token) { lwm2m_transaction_t * transacP; int result; LOG_ARG("method: %d, altPath: \"%s\", mID: %d, token_len: %d", method, altPath, mID, token_len); LOG_URI(uriP); // no transactions without peer if (NULL == sessionH) return NULL; transacP = (lwm2m_transaction_t *)lwm2m_malloc(sizeof(lwm2m_transaction_t)); if (NULL == transacP) return NULL; memset(transacP, 0, sizeof(lwm2m_transaction_t)); transacP->message = lwm2m_malloc(sizeof(coap_packet_t)); if (NULL == transacP->message) goto error; coap_init_message(transacP->message, COAP_TYPE_CON, method, mID); transacP->peerH = sessionH; transacP->mID = mID; if (altPath != NULL) { // TODO: Support multi-segment alternative path coap_set_header_uri_path_segment(transacP->message, altPath + 1); } if (NULL != uriP) { char stringID[LWM2M_STRING_ID_MAX_LEN]; result = utils_intToText(uriP->objectId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN); if (result == 0) goto error; stringID[result] = 0; coap_set_header_uri_path_segment(transacP->message, stringID); if (LWM2M_URI_IS_SET_INSTANCE(uriP)) { result = utils_intToText(uriP->instanceId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN); if (result == 0) goto error; stringID[result] = 0; coap_set_header_uri_path_segment(transacP->message, stringID); } else { if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { coap_set_header_uri_path_segment(transacP->message, NULL); } } if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { result = utils_intToText(uriP->resourceId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN); if (result == 0) goto error; stringID[result] = 0; coap_set_header_uri_path_segment(transacP->message, stringID); } } if (0 < token_len) { if (NULL != token) { coap_set_header_token(transacP->message, token, token_len); } else { // generate a token uint8_t temp_token[COAP_TOKEN_LEN]; time_t tv_sec = lwm2m_gettime(); // initialize first 6 bytes, leave the last 2 random temp_token[0] = mID; temp_token[1] = mID >> 8; temp_token[2] = tv_sec; temp_token[3] = tv_sec >> 8; temp_token[4] = tv_sec >> 16; temp_token[5] = tv_sec >> 24; // use just the provided amount of bytes coap_set_header_token(transacP->message, temp_token, token_len); } } LOG("Exiting on success"); return transacP; error: LOG("Exiting on failure"); lwm2m_free(transacP); return NULL; }
void reset_bootstrap_timer(lwm2m_context_t * context) { context->bsStart = lwm2m_gettime(); }
int main(int argc, char *argv[]) { client_data_t data; int result; lwm2m_context_t * lwm2mH = NULL; int i; const char * localPort = "56830"; const char * server = NULL; const char * serverPort = LWM2M_STANDARD_PORT_STR; char * name = "testlwm2mclient"; int lifetime = 300; int batterylevelchanging = 0; time_t reboot_time = 0; int opt; bool bootstrapRequested = false; bool serverPortChanged = false; #ifdef LWM2M_BOOTSTRAP lwm2m_client_state_t previousState = STATE_INITIAL; #endif char * pskId = NULL; char * psk = NULL; uint16_t pskLen = -1; char * pskBuffer = NULL; /* * The function start by setting up the command line interface (which may or not be useful depending on your project) * * This is an array of commands describes as { name, description, long description, callback, userdata }. * The firsts tree are easy to understand, the callback is the function that will be called when this command is typed * and in the last one will be stored the lwm2m context (allowing access to the server settings and the objects). */ command_desc_t commands[] = { {"list", "List known servers.", NULL, prv_output_servers, NULL}, {"change", "Change the value of resource.", " change URI [DATA]\r\n" " URI: uri of the resource such as /3/0, /3/0/2\r\n" " DATA: (optional) new value\r\n", prv_change, NULL}, {"update", "Trigger a registration update", " update SERVER\r\n" " SERVER: short server id such as 123\r\n", prv_update, NULL}, #ifdef LWM2M_BOOTSTRAP {"bootstrap", "Initiate a DI bootstrap process", NULL, prv_initiate_bootstrap, NULL}, {"dispb", "Display current backup of objects/instances/resources\r\n" "\t(only security and server objects are backupped)", NULL, prv_display_backup, NULL}, #endif {"ls", "List Objects and Instances", NULL, prv_object_list, NULL}, {"disp", "Display current objects/instances/resources", NULL, prv_display_objects, NULL}, {"dump", "Dump an Object", "dump URI" "URI: uri of the Object or Instance such as /3/0, /1\r\n", prv_object_dump, NULL}, {"add", "Add support of object 1024", NULL, prv_add, NULL}, {"rm", "Remove support of object 1024", NULL, prv_remove, NULL}, {"quit", "Quit the client gracefully.", NULL, prv_quit, NULL}, {"^C", "Quit the client abruptly (without sending a de-register message).", NULL, NULL, NULL}, COMMAND_END_LIST }; memset(&data, 0, sizeof(client_data_t)); data.addressFamily = AF_INET6; opt = 1; while (opt < argc) { if (argv[opt] == NULL || argv[opt][0] != '-' || argv[opt][2] != 0) { print_usage(); return 0; } switch (argv[opt][1]) { case 'b': bootstrapRequested = true; if (!serverPortChanged) serverPort = LWM2M_BSSERVER_PORT_STR; break; case 'c': batterylevelchanging = 1; break; case 't': opt++; if (opt >= argc) { print_usage(); return 0; } if (1 != sscanf(argv[opt], "%d", &lifetime)) { print_usage(); return 0; } break; #ifdef WITH_TINYDTLS case 'i': opt++; if (opt >= argc) { print_usage(); return 0; } pskId = argv[opt]; break; case 's': opt++; if (opt >= argc) { print_usage(); return 0; } psk = argv[opt]; break; #endif case 'n': opt++; if (opt >= argc) { print_usage(); return 0; } name = argv[opt]; break; case 'l': opt++; if (opt >= argc) { print_usage(); return 0; } localPort = argv[opt]; break; case 'h': opt++; if (opt >= argc) { print_usage(); return 0; } server = argv[opt]; break; case 'p': opt++; if (opt >= argc) { print_usage(); return 0; } serverPort = argv[opt]; serverPortChanged = true; break; case '4': data.addressFamily = AF_INET; break; default: print_usage(); return 0; } opt += 1; } if (!server) { server = (AF_INET == data.addressFamily ? DEFAULT_SERVER_IPV4 : DEFAULT_SERVER_IPV6); } /* *This call an internal function that create an IPV6 socket on the port 5683. */ fprintf(stderr, "Trying to bind LWM2M Client to port %s\r\n", localPort); data.sock = create_socket(localPort, data.addressFamily); if (data.sock < 0) { fprintf(stderr, "Failed to open socket: %d %s\r\n", errno, strerror(errno)); return -1; } /* * Now the main function fill an array with each object, this list will be later passed to liblwm2m. * Those functions are located in their respective object file. */ #ifdef WITH_TINYDTLS if (psk != NULL) { pskLen = strlen(psk) / 2; pskBuffer = malloc(pskLen); if (NULL == pskBuffer) { fprintf(stderr, "Failed to create PSK binary buffer\r\n"); return -1; } // Hex string to binary char *h = psk; char *b = pskBuffer; char xlate[] = "0123456789ABCDEF"; for ( ; *h; h += 2, ++b) { char *l = strchr(xlate, toupper(*h)); char *r = strchr(xlate, toupper(*(h+1))); if (!r || !l) { fprintf(stderr, "Failed to parse Pre-Shared-Key HEXSTRING\r\n"); return -1; } *b = ((l - xlate) << 4) + (r - xlate); } } #endif char serverUri[50]; int serverId = 123; sprintf (serverUri, "coap://%s:%s", server, serverPort); #ifdef LWM2M_BOOTSTRAP objArray[0] = get_security_object(serverId, serverUri, pskId, pskBuffer, pskLen, bootstrapRequested); #else objArray[0] = get_security_object(serverId, serverUri, pskId, pskBuffer, pskLen, false); #endif if (NULL == objArray[0]) { fprintf(stderr, "Failed to create security object\r\n"); return -1; } data.securityObjP = objArray[0]; objArray[1] = get_server_object(serverId, "U", lifetime, false); if (NULL == objArray[1]) { fprintf(stderr, "Failed to create server object\r\n"); return -1; } objArray[2] = get_object_device(); if (NULL == objArray[2]) { fprintf(stderr, "Failed to create Device object\r\n"); return -1; } objArray[3] = get_object_firmware(); if (NULL == objArray[3]) { fprintf(stderr, "Failed to create Firmware object\r\n"); return -1; } objArray[4] = get_object_location(); if (NULL == objArray[4]) { fprintf(stderr, "Failed to create location object\r\n"); return -1; } objArray[5] = get_test_object(); if (NULL == objArray[5]) { fprintf(stderr, "Failed to create test object\r\n"); return -1; } objArray[6] = get_object_conn_m(); if (NULL == objArray[6]) { fprintf(stderr, "Failed to create connectivity monitoring object\r\n"); return -1; } objArray[7] = get_object_conn_s(); if (NULL == objArray[7]) { fprintf(stderr, "Failed to create connectivity statistics object\r\n"); return -1; } int instId = 0; objArray[8] = acc_ctrl_create_object(); if (NULL == objArray[8]) { fprintf(stderr, "Failed to create Access Control object\r\n"); return -1; } else if (acc_ctrl_obj_add_inst(objArray[8], instId, 3, 0, serverId)==false) { fprintf(stderr, "Failed to create Access Control object instance\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 0, 0b000000000001111)==false) { fprintf(stderr, "Failed to create Access Control ACL default resource\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 999, 0b000000000000001)==false) { fprintf(stderr, "Failed to create Access Control ACL resource for serverId: 999\r\n"); return -1; } /* * The liblwm2m library is now initialized with the functions that will be in * charge of communication */ lwm2mH = lwm2m_init(&data); if (NULL == lwm2mH) { fprintf(stderr, "lwm2m_init() failed\r\n"); return -1; } #ifdef WITH_TINYDTLS data.lwm2mH = lwm2mH; #endif /* * We configure the liblwm2m library with the name of the client - which shall be unique for each client - * the number of objects we will be passing through and the objects array */ result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray); if (result != 0) { fprintf(stderr, "lwm2m_configure() failed: 0x%X\r\n", result); return -1; } signal(SIGINT, handle_sigint); /** * Initialize value changed callback. */ init_value_change(lwm2mH); /* * As you now have your lwm2m context complete you can pass it as an argument to all the command line functions * precedently viewed (first point) */ for (i = 0 ; commands[i].name != NULL ; i++) { commands[i].userData = (void *)lwm2mH; } fprintf(stdout, "LWM2M Client \"%s\" started on port %s\r\n", name, localPort); fprintf(stdout, "> "); fflush(stdout); /* * We now enter in a while loop that will handle the communications from the server */ while (0 == g_quit) { struct timeval tv; fd_set readfds; if (g_reboot) { time_t tv_sec; tv_sec = lwm2m_gettime(); if (0 == reboot_time) { reboot_time = tv_sec + 5; } if (reboot_time < tv_sec) { /* * Message should normally be lost with reboot ... */ fprintf(stderr, "reboot time expired, rebooting ..."); system_reboot(); } else { tv.tv_sec = reboot_time - tv_sec; } } else if (batterylevelchanging) { update_battery_level(lwm2mH); tv.tv_sec = 5; } else { tv.tv_sec = 60; } tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(data.sock, &readfds); FD_SET(STDIN_FILENO, &readfds); /* * This function does two things: * - first it does the work needed by liblwm2m (eg. (re)sending some packets). * - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction * (eg. retransmission) and the time between the next operation */ result = lwm2m_step(lwm2mH, &(tv.tv_sec)); if (result != 0) { fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result); if(previousState == STATE_BOOTSTRAPPING) { #ifdef WITH_LOGS fprintf(stdout, "[BOOTSTRAP] restore security and server objects\r\n"); #endif prv_connections_free(lwm2mH); prv_restore_objects(lwm2mH); lwm2mH->state = STATE_INITIAL; } else return -1; } #ifdef LWM2M_BOOTSTRAP update_bootstrap_info(&previousState, lwm2mH); #endif /* * This part will set up an interruption until an event happen on SDTIN or the socket until "tv" timed out (set * with the precedent function) */ result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "Error in select(): %d %s\r\n", errno, strerror(errno)); } } else if (result > 0) { uint8_t buffer[MAX_PACKET_SIZE]; int numBytes; /* * If an event happens on the socket */ if (FD_ISSET(data.sock, &readfds)) { struct sockaddr_storage addr; socklen_t addrLen; addrLen = sizeof(addr); /* * We retrieve the data received */ numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen); if (0 > numBytes) { fprintf(stderr, "Error in recvfrom(): %d %s\r\n", errno, strerror(errno)); } else if (0 < numBytes) { char s[INET6_ADDRSTRLEN]; in_port_t port; #ifdef WITH_TINYDTLS dtls_connection_t * connP; #else connection_t * connP; #endif if (AF_INET == addr.ss_family) { struct sockaddr_in *saddr = (struct sockaddr_in *)&addr; inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN); port = saddr->sin_port; } else if (AF_INET6 == addr.ss_family) { struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&addr; inet_ntop(saddr->sin6_family, &saddr->sin6_addr, s, INET6_ADDRSTRLEN); port = saddr->sin6_port; } fprintf(stderr, "%d bytes received from [%s]:%hu\r\n", numBytes, s, ntohs(port)); /* * Display it in the STDERR */ output_buffer(stderr, buffer, numBytes, 0); connP = connection_find(data.connList, &addr, addrLen); if (connP != NULL) { /* * Let liblwm2m respond to the query depending on the context */ #ifdef WITH_TINYDTLS int result = connection_handle_packet(connP, buffer, numBytes); if (0 != result) { printf("error handling message %d\n",result); } #else lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP); #endif conn_s_updateRxStatistic(objArray[7], numBytes, false); } else { fprintf(stderr, "received bytes ignored!\r\n"); } } } /* * If the event happened on the SDTIN */ else if (FD_ISSET(STDIN_FILENO, &readfds)) { numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE - 1); if (numBytes > 1) { buffer[numBytes] = 0; /* * We call the corresponding callback of the typed command passing it the buffer for further arguments */ handle_command(commands, (char*)buffer); } if (g_quit == 0) { fprintf(stdout, "\r\n> "); fflush(stdout); } else { fprintf(stdout, "\r\n"); } } } } /* * Finally when the loop is left smoothly - asked by user in the command line interface - we unregister our client from it */ if (g_quit == 1) { #ifdef LWM2M_BOOTSTRAP close_backup_object(); #endif lwm2m_close(lwm2mH); } close(data.sock); connection_free(data.connList); clean_security_object(objArray[0]); lwm2m_free(objArray[0]); clean_server_object(objArray[1]); lwm2m_free(objArray[1]); free_object_device(objArray[2]); free_object_firmware(objArray[3]); free_object_location(objArray[4]); free_test_object(objArray[5]); free_object_conn_m(objArray[6]); free_object_conn_s(objArray[7]); acl_ctrl_free_object(objArray[8]); #ifdef MEMORY_TRACE if (g_quit == 1) { trace_print(0, 1); } #endif return 0; }
int transaction_send(lwm2m_context_t * contextP, lwm2m_transaction_t * transacP) { bool maxRetriesReached = false; LOG("Entering"); if (transacP->buffer == NULL) { transacP->buffer_len = coap_serialize_get_size(transacP->message); if (transacP->buffer_len == 0) { transaction_remove(contextP, transacP); return COAP_500_INTERNAL_SERVER_ERROR; } transacP->buffer = (uint8_t*)lwm2m_malloc(transacP->buffer_len); if (transacP->buffer == NULL) { transaction_remove(contextP, transacP); return COAP_500_INTERNAL_SERVER_ERROR; } transacP->buffer_len = coap_serialize_message(transacP->message, transacP->buffer); if (transacP->buffer_len == 0) { lwm2m_free(transacP->buffer); transacP->buffer = NULL; transaction_remove(contextP, transacP); return COAP_500_INTERNAL_SERVER_ERROR; } } if (!transacP->ack_received) { long unsigned timeout; if (0 == transacP->retrans_counter) { time_t tv_sec = lwm2m_gettime(); if (0 <= tv_sec) { transacP->retrans_time = tv_sec + COAP_RESPONSE_TIMEOUT; transacP->retrans_counter = 1; timeout = 0; } else { maxRetriesReached = true; } } else { timeout = COAP_RESPONSE_TIMEOUT << (transacP->retrans_counter - 1); } if (COAP_MAX_RETRANSMIT + 1 >= transacP->retrans_counter) { (void)lwm2m_buffer_send(transacP->peerH, transacP->buffer, transacP->buffer_len, contextP->userData); transacP->retrans_time += timeout; transacP->retrans_counter += 1; } else { maxRetriesReached = true; } } if (transacP->ack_received || maxRetriesReached) { if (transacP->callback) { transacP->callback(transacP, NULL); } transaction_remove(contextP, transacP); return -1; } return 0; }
bool transaction_handleResponse(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response) { bool found = false; bool reset = false; lwm2m_transaction_t * transacP; LOG("Entering"); transacP = contextP->transactionList; while (NULL != transacP) { if (lwm2m_session_is_equal(fromSessionH, transacP->peerH, contextP->userData) == true) { if (!transacP->ack_received) { if ((COAP_TYPE_ACK == message->type) || (COAP_TYPE_RST == message->type)) { if (transacP->mID == message->mid) { found = true; transacP->ack_received = true; reset = COAP_TYPE_RST == message->type; } } } if (reset || prv_checkFinished(transacP, message)) { // HACK: If a message is sent from the monitor callback, // it will arrive before the registration ACK. // So we resend transaction that were denied for authentication reason. if (!reset) { if (COAP_TYPE_CON == message->type && NULL != response) { coap_init_message(response, COAP_TYPE_ACK, 0, message->mid); message_send(contextP, response, fromSessionH); } if ((COAP_401_UNAUTHORIZED == message->code) && (COAP_MAX_RETRANSMIT > transacP->retrans_counter)) { transacP->ack_received = false; transacP->retrans_time += COAP_RESPONSE_TIMEOUT; return true; } } if (transacP->callback != NULL) { transacP->callback(transacP, message); } transaction_remove(contextP, transacP); return true; } // if we found our guy, exit if (found) { time_t tv_sec = lwm2m_gettime(); if (0 <= tv_sec) { transacP->retrans_time = tv_sec; } if (transacP->response_timeout) { transacP->retrans_time += transacP->response_timeout; } else { transacP->retrans_time += COAP_RESPONSE_TIMEOUT * transacP->retrans_counter; } return true; } } transacP = transacP->next; } return false; }
int lwm2m_step(lwm2m_context_t * contextP, time_t * timeoutP) { time_t tv_sec; int result; LOG_ARG("timeoutP: %" PRId64, *timeoutP); tv_sec = lwm2m_gettime(); if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR; #ifdef LWM2M_CLIENT_MODE LOG_ARG("State: %s", STR_STATE(contextP->state)); // state can also be modified in bootstrap_handleCommand(). next_step: switch (contextP->state) { case STATE_INITIAL: if (0 != prv_refreshServerList(contextP)) return COAP_503_SERVICE_UNAVAILABLE; if (contextP->serverList != NULL) { contextP->state = STATE_REGISTER_REQUIRED; } else { // Bootstrapping contextP->state = STATE_BOOTSTRAP_REQUIRED; } goto next_step; break; case STATE_BOOTSTRAP_REQUIRED: #ifdef LWM2M_BOOTSTRAP if (contextP->bootstrapServerList != NULL) { bootstrap_start(contextP); contextP->state = STATE_BOOTSTRAPPING; bootstrap_step(contextP, tv_sec, timeoutP); } else #endif { return COAP_503_SERVICE_UNAVAILABLE; } break; #ifdef LWM2M_BOOTSTRAP case STATE_BOOTSTRAPPING: switch (bootstrap_getStatus(contextP)) { case STATE_BS_FINISHED: contextP->state = STATE_INITIAL; goto next_step; break; case STATE_BS_FAILED: return COAP_503_SERVICE_UNAVAILABLE; default: // keep on waiting bootstrap_step(contextP, tv_sec, timeoutP); break; } break; #endif case STATE_REGISTER_REQUIRED: result = registration_start(contextP); if (COAP_NO_ERROR != result) return result; contextP->state = STATE_REGISTERING; break; case STATE_REGISTERING: { switch (registration_getStatus(contextP)) { case STATE_REGISTERED: contextP->state = STATE_READY; break; case STATE_REG_FAILED: // TODO avoid infinite loop by checking the bootstrap info is different contextP->state = STATE_BOOTSTRAP_REQUIRED; goto next_step; break; case STATE_REG_PENDING: default: // keep on waiting break; } } break; case STATE_READY: if (registration_getStatus(contextP) == STATE_REG_FAILED) { // TODO avoid infinite loop by checking the bootstrap info is different contextP->state = STATE_BOOTSTRAP_REQUIRED; goto next_step; break; } break; default: // do nothing break; } observe_step(contextP, tv_sec, timeoutP); #endif registration_step(contextP, tv_sec, timeoutP); transaction_step(contextP, tv_sec, timeoutP); LOG_ARG("Final timeoutP: %" PRId64, *timeoutP); #ifdef LWM2M_CLIENT_MODE LOG_ARG("Final state: %s", STR_STATE(contextP->state)); #endif return 0; }
coap_status_t handle_registration_request(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response) { coap_status_t result; time_t tv_sec; tv_sec = lwm2m_gettime(); if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR; switch(message->code) { case COAP_POST: { char * name = NULL; uint32_t lifetime; char * msisdn; char * altPath; lwm2m_binding_t binding; lwm2m_client_object_t * objects; lwm2m_client_t * clientP; char location[MAX_LOCATION_LENGTH]; if (0 != prv_getParameters(message->uri_query, &name, &lifetime, &msisdn, &binding)) { return COAP_400_BAD_REQUEST; } objects = prv_decodeRegisterPayload(message->payload, message->payload_len, &altPath); switch (uriP->flag & LWM2M_URI_MASK_ID) { case 0: // Register operation if (objects == NULL) { lwm2m_free(name); if (msisdn != NULL) lwm2m_free(msisdn); return COAP_400_BAD_REQUEST; } // Endpoint client name is mandatory if (name == NULL) { if (msisdn != NULL) lwm2m_free(msisdn); return COAP_400_BAD_REQUEST; } if (lifetime == 0) { lifetime = LWM2M_DEFAULT_LIFETIME; } clientP = prv_getClientByName(contextP, name); if (clientP != NULL) { // we reset this registration lwm2m_free(clientP->name); if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); if (clientP->altPath != NULL) lwm2m_free(clientP->altPath); prv_freeClientObjectList(clientP->objectList); clientP->objectList = NULL; } else { clientP = (lwm2m_client_t *)lwm2m_malloc(sizeof(lwm2m_client_t)); if (clientP == NULL) { lwm2m_free(name); lwm2m_free(altPath); if (msisdn != NULL) lwm2m_free(msisdn); prv_freeClientObjectList(objects); return COAP_500_INTERNAL_SERVER_ERROR; } memset(clientP, 0, sizeof(lwm2m_client_t)); clientP->internalID = lwm2m_list_newId((lwm2m_list_t *)contextP->clientList); contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_ADD(contextP->clientList, clientP); } clientP->name = name; clientP->binding = binding; clientP->msisdn = msisdn; clientP->altPath = altPath; clientP->lifetime = lifetime; clientP->endOfLife = tv_sec + lifetime; clientP->objectList = objects; clientP->sessionH = fromSessionH; if (prv_getLocationString(clientP->internalID, location) == 0) { prv_freeClient(clientP); return COAP_500_INTERNAL_SERVER_ERROR; } if (coap_set_header_location_path(response, location) == 0) { prv_freeClient(clientP); return COAP_500_INTERNAL_SERVER_ERROR; } if (contextP->monitorCallback != NULL) { contextP->monitorCallback(clientP->internalID, NULL, CREATED_2_01, NULL, 0, contextP->monitorUserData); } result = COAP_201_CREATED; break; case LWM2M_URI_FLAG_OBJECT_ID: clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, uriP->objectId); if (clientP == NULL) return COAP_404_NOT_FOUND; // Endpoint client name MUST NOT be present if (name != NULL) { lwm2m_free(name); if (msisdn != NULL) lwm2m_free(msisdn); return COAP_400_BAD_REQUEST; } if (binding != BINDING_UNKNOWN) { clientP->binding = binding; } if (msisdn != NULL) { if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); clientP->msisdn = msisdn; } if (lifetime != 0) { clientP->lifetime = lifetime; } // client IP address, port or MSISDN may have changed clientP->sessionH = fromSessionH; if (objects != NULL) { lwm2m_observation_t * observationP; // remove observations on object/instance no longer existing observationP = clientP->observationList; while (observationP != NULL) { lwm2m_client_object_t * objP; lwm2m_observation_t * nextP; nextP = observationP->next; objP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objects, observationP->uri.objectId); if (objP == NULL) { observationP->callback(clientP->internalID, &observationP->uri, COAP_202_DELETED, NULL, 0, observationP->userData); observation_remove(clientP, observationP); } else { if ((observationP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0) { if (lwm2m_list_find((lwm2m_list_t *)objP->instanceList, observationP->uri.instanceId) == NULL) { observationP->callback(clientP->internalID, &observationP->uri, COAP_202_DELETED, NULL, 0, observationP->userData); observation_remove(clientP, observationP); } } } observationP = nextP; } prv_freeClientObjectList(clientP->objectList); clientP->objectList = objects; } clientP->endOfLife = tv_sec + clientP->lifetime; if (contextP->monitorCallback != NULL) { contextP->monitorCallback(clientP->internalID, NULL, COAP_204_CHANGED, NULL, 0, contextP->monitorUserData); } result = COAP_204_CHANGED; break; default: return COAP_400_BAD_REQUEST; } } break; case COAP_DELETE: { lwm2m_client_t * clientP; if ((uriP->flag & LWM2M_URI_MASK_ID) != LWM2M_URI_FLAG_OBJECT_ID) return COAP_400_BAD_REQUEST; contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, uriP->objectId, &clientP); if (clientP == NULL) return COAP_400_BAD_REQUEST; if (contextP->monitorCallback != NULL) { contextP->monitorCallback(clientP->internalID, NULL, DELETED_2_02, NULL, 0, contextP->monitorUserData); } prv_freeClient(clientP); result = COAP_202_DELETED; } break; default: return COAP_400_BAD_REQUEST; } return result; }
int lwm2mclient_main() { client_data_t data; int result; lwm2m_context_t * lwm2mH = NULL; int i; const char * localPort = "56830"; //const char * server = "beta-devices.zatar.com"; const char * server = "leshan.eclipse.org"; //const char * server = "5.39.83.206"; const char * serverPort = LWM2M_STANDARD_PORT_STR; char * name = "ZebraBlr"; int lifetime = 300; int batterylevelchanging = 0; time_t reboot_time = 0; int opt; bool bootstrapRequested = false; #ifdef LWM2M_BOOTSTRAP lwm2m_bootstrap_state_t previousBootstrapState = NOT_BOOTSTRAPPED; #endif /* * The function start by setting up the command line interface (which may or not be useful depending on your project) * * This is an array of commands describes as { name, description, long description, callback, userdata }. * The firsts tree are easy to understand, the callback is the function that will be called when this command is typed * and in the last one will be stored the lwm2m context (allowing access to the server settings and the objects). */ command_desc_t commands[] = { {"list", "List known servers.", NULL, prv_output_servers, NULL}, {"change", "Change the value of resource.", " change URI [DATA]\r\n" " URI: uri of the resource such as /3/0, /3/0/2\r\n" " DATA: (optional) new value\r\n", prv_change, NULL}, {"update", "Trigger a registration update", " update SERVER\r\n" " SERVER: short server id such as 123\r\n", prv_update, NULL}, #ifdef LWM2M_BOOTSTRAP {"bootstrap", "Initiate a DI bootstrap process", NULL, prv_initiate_bootstrap, NULL}, {"disp", "Display current objects/instances/resources", NULL, prv_display_objects, NULL}, {"dispb", "Display current backup of objects/instances/resources\r\n" "\t(only security and server objects are backupped)", NULL, prv_display_backup, NULL}, #endif {"ls", "List Objects and Instances", NULL, prv_object_list, NULL}, {"dump", "Dump an Object", "dump URI" "URI: uri of the Object or Instance such as /3/0, /1\r\n", prv_object_dump, NULL}, {"quit", "Quit the client gracefully.", NULL, prv_quit, NULL}, {"^C", "Quit the client abruptly (without sending a de-register message).", NULL, NULL, NULL}, COMMAND_END_LIST }; memset(&data, 0, sizeof(client_data_t)); /* Zebra change: Reddy while ((opt = getopt(argc, argv, "bcl:n:p:t:h:")) != -1) { switch (opt) { case 'b': bootstrapRequested = true; break; case 'c': batterylevelchanging = 1; break; case 't': sscanf(optarg, "%d", &lifetime); break; case 'n': name = optarg; break; case 'l': localPort = optarg; break; case 'h': server = optarg; break; case 'p': serverPort = optarg; break; default: print_usage(); return 0; } } */ /* *This call an internal function that create an IPV6 socket on the port 5683. */ fprintf(stderr, "Trying to bind LWM2M Client to port %s\r\n", localPort); data.sock = create_socket(localPort); if (data.sock < 0) { fprintf(stderr, "Failed to open socket: %d\r\n", errno); return -1; } /* * Now the main function fill an array with each object, this list will be later passed to liblwm2m. * Those functions are located in their respective object file. */ char serverUri[50]; int serverId = 123; sprintf (serverUri, "coap://%s:%s", server, serverPort); fprintf(stderr, " LWM2M serverUri %s\r\n", serverUri); #ifdef LWM2M_BOOTSTRAP objArray[0] = get_security_object(serverId, serverUri, bootstrapRequested); #else objArray[0] = get_security_object(serverId, serverUri, false); #endif if (NULL == objArray[0]) { fprintf(stderr, "Failed to create security object\r\n"); return -1; } data.securityObjP = objArray[0]; objArray[1] = get_server_object(serverId, "U", lifetime, false); if (NULL == objArray[1]) { fprintf(stderr, "Failed to create server object\r\n"); return -1; } objArray[2] = get_object_device(); if (NULL == objArray[2]) { fprintf(stderr, "Failed to create Device object\r\n"); return -1; } objArray[3] = get_object_firmware(); if (NULL == objArray[3]) { fprintf(stderr, "Failed to create Firmware object\r\n"); return -1; } objArray[4] = get_object_location(); if (NULL == objArray[4]) { fprintf(stderr, "Failed to create location object\r\n"); return -1; } objArray[5] = get_test_object(); if (NULL == objArray[5]) { fprintf(stderr, "Failed to create test object\r\n"); return -1; } objArray[6] = get_object_conn_m(); if (NULL == objArray[6]) { fprintf(stderr, "Failed to create connectivity monitoring object\r\n"); return -1; } objArray[7] = get_object_conn_s(); if (NULL == objArray[7]) { fprintf(stderr, "Failed to create connectivity statistics object\r\n"); return -1; } int instId = 0; objArray[8] = acc_ctrl_create_object(); if (NULL == objArray[8]) { fprintf(stderr, "Failed to create Access Control object\r\n"); return -1; } else if (acc_ctrl_obj_add_inst(objArray[8], instId, 3, 0, serverId)==false) { fprintf(stderr, "Failed to create Access Control object instance\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 0, 0b000000000001111)==false) { fprintf(stderr, "Failed to create Access Control ACL default resource\r\n"); return -1; } else if (acc_ctrl_oi_add_ac_val(objArray[8], instId, 999, 0b000000000000001)==false) { fprintf(stderr, "Failed to create Access Control ACL resource for serverId: 999\r\n"); return -1; } /* * The liblwm2m library is now initialized with the functions that will be in * charge of communication */ lwm2mH = lwm2m_init(prv_connect_server, prv_buffer_send, &data); if (NULL == lwm2mH) { fprintf(stderr, "lwm2m_init() failed\r\n"); return -1; } #ifdef LWM2M_BOOTSTRAP /* * Bootstrap state initialization */ if (bootstrapRequested) { lwm2mH->bsState = BOOTSTRAP_REQUESTED; } else { lwm2mH->bsState = NOT_BOOTSTRAPPED; } #endif /* * We configure the liblwm2m library with the name of the client - which shall be unique for each client - * the number of objects we will be passing through and the objects array */ result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray); if (result != 0) { fprintf(stderr, "lwm2m_configure() failed: 0x%X\r\n", result); return -1; } signal(SIGINT, handle_sigint); /* * This function start your client to the LWM2M servers */ result = lwm2m_start(lwm2mH); if (result != 0) { fprintf(stderr, "lwm2m_start() failed: 0x%X\r\n", result); return -1; } /** * Initialize value changed callback. */ init_value_change(lwm2mH); /* * As you now have your lwm2m context complete you can pass it as an argument to all the command line functions * precedently viewed (first point) */ for (i = 0 ; commands[i].name != NULL ; i++) { commands[i].userData = (void *)lwm2mH; } fprintf(stdout, "LWM2M Client \"%s\" started on port %s\r\n", name, localPort); fprintf(stdout, "> "); fflush(stdout); /* * We now enter in a while loop that will handle the communications from the server */ while (0 == g_quit) { struct timeval tv; fd_set readfds; if (g_reboot) { time_t tv_sec; tv_sec = lwm2m_gettime(); if (0 == reboot_time) { reboot_time = tv_sec + 5; } if (reboot_time < tv_sec) { /* * Message should normally be lost with reboot ... */ fprintf(stderr, "reboot time expired, rebooting ..."); system_reboot(); } else { tv.tv_sec = reboot_time - tv_sec; } } else if (batterylevelchanging) { update_battery_level(lwm2mH); tv.tv_sec = 5; } else { tv.tv_sec = 60; } tv.tv_usec = 0; FD_ZERO(&readfds); FD_SET(data.sock, &readfds); FD_SET(STDIN_FILENO, &readfds); /* * This function does two things: * - first it does the work needed by liblwm2m (eg. (re)sending some packets). * - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction * (eg. retransmission) and the time between the next operation */ result = lwm2m_step(lwm2mH, &(tv.tv_sec)); if (result != 0) { fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result); return -1; } #ifdef LWM2M_BOOTSTRAP update_bootstrap_info(&previousBootstrapState, lwm2mH); #endif /* * This part will set up an interruption until an event happen on SDTIN or the socket until "tv" timed out (set * with the precedent function) */ //result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "Error in select(): %d\r\n", errno); } } else if (result > 0) { uint8_t buffer[MAX_PACKET_SIZE]; int numBytes; /* * If an event happens on the socket */ if (FD_ISSET(data.sock, &readfds)) { //struct sockaddr_storage addr; struct sockaddr addr; uint32 addrLen; addrLen = sizeof(addr); /* * We retrieve the data received */ //numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen); numBytes = recvfrom(data.sock, buffer, MAX_PACKET_SIZE,0); if (0 > numBytes) { fprintf(stderr, "Error in recvfrom(): %d\r\n", errno); } else if (0 < numBytes) { char s[INET6_ADDRSTRLEN]; uint16 port; connection_t * connP; if (AF_INET == addr.sa_family) { struct sockaddr_in *saddr = (struct sockaddr_in *)&addr; //inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN); port = saddr->sin_port; } //else if (AF_INET6 == addr.ss_family) //{ // struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&addr; // inet_ntop(saddr->sin6_family, &saddr->sin6_addr, s, INET6_ADDRSTRLEN); // port = saddr->sin6_port; // } //fprintf(stderr, "%d bytes received from [%s]:%hu\r\n", numBytes, s, ntohs(port)); /* * Display it in the STDERR */ output_buffer(stderr, buffer, numBytes, 0); connP = connection_find(data.connList, &addr, addrLen); if (connP != NULL) { /* * Let liblwm2m respond to the query depending on the context */ lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP); conn_s_updateRxStatistic(objArray[7], numBytes, false); } else { fprintf(stderr, "received bytes ignored!\r\n"); } } } /* * If the event happened on the SDTIN */ else if (FD_ISSET(STDIN_FILENO, &readfds)) { numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE - 1); if (numBytes > 1) { buffer[numBytes] = 0; fprintf(stderr, "STDIN %d bytes '%s'\r\n> ", numBytes, buffer); /* * We call the corresponding callback of the typed command passing it the buffer for further arguments */ handle_command(commands, (char*)buffer); } if (g_quit == 0) { fprintf(stdout, "\r\n> "); fflush(stdout); } else { fprintf(stdout, "\r\n"); } } } } /* * Finally when the loop is left smoothly - asked by user in the command line interface - we unregister our client from it */ if (g_quit == 1) { #ifdef LWM2M_BOOTSTRAP close_backup_object(); #endif lwm2m_close(lwm2mH); } close(data.sock); connection_free(data.connList); return 0; }