void ind_cxn_bundle_add_handle(connection_t *cxn, of_object_t *obj) { uint32_t bundle_id; uint16_t flags; of_octets_t data; of_bundle_add_msg_bundle_id_get(obj, &bundle_id); of_bundle_add_msg_flags_get(obj, &flags); of_bundle_add_msg_data_get(obj, &data); bundle_t *bundle = find_bundle(cxn, bundle_id); if (bundle == NULL) { indigo_cxn_send_error_reply( cxn->cxn_id, obj, OF_ERROR_TYPE_BUNDLE_FAILED, OFPBFC_BAD_ID); return; } /* Validate length */ if (data.bytes < OF_MESSAGE_HEADER_LENGTH || data.bytes != of_message_length_get(data.data)) { indigo_cxn_send_error_reply( cxn->cxn_id, obj, OF_ERROR_TYPE_BUNDLE_FAILED, OFPBFC_MSG_BAD_LEN); AIM_LOG_WARN("Inconsistent bundled message length", bundle->id); return; } /* Limit number of messages in the bundle */ if (bundle->count >= OFCONNECTIONMANAGER_CONFIG_MAX_BUNDLE_MSGS) { indigo_cxn_send_error_reply( cxn->cxn_id, obj, OF_ERROR_TYPE_BUNDLE_FAILED, OFPBFC_MSG_TOO_MANY); AIM_LOG_WARN("Exceeded maximum number (%u) of messages in bundle %u", OFCONNECTIONMANAGER_CONFIG_MAX_BUNDLE_MSGS, bundle->id); return; } /* Limit amount of memory used by the bundle */ if ((bundle->bytes + data.bytes) > OFCONNECTIONMANAGER_CONFIG_MAX_BUNDLE_BYTES) { indigo_cxn_send_error_reply( cxn->cxn_id, obj, OF_ERROR_TYPE_BUNDLE_FAILED, OFPBFC_MSG_TOO_MANY); AIM_LOG_WARN("Exceeded maximum size (%u bytes) of messages in bundle %u", OFCONNECTIONMANAGER_CONFIG_MAX_BUNDLE_BYTES, bundle->id); return; } if (bundle->count == bundle->allocated) { /* Resize array */ uint32_t new_allocated = (bundle->allocated == 0 ? 1 : bundle->allocated * 2); bundle->msgs = aim_realloc(bundle->msgs, sizeof(*bundle->msgs) * new_allocated); bundle->allocated = new_allocated; } bundle->msgs[bundle->count++] = aim_memdup(data.data, data.bytes); bundle->bytes += data.bytes; }
static void handle_lua_upload(indigo_cxn_id_t cxn_id, of_object_t *msg) { of_octets_t data; uint16_t flags; of_str64_t filename; of_bsn_lua_upload_data_get(msg, &data); of_bsn_lua_upload_flags_get(msg, &flags); of_bsn_lua_upload_filename_get(msg, &filename); /* Ensure filename is null terminated */ filename[63] = 0; if (xbuf_length(&upload_chunks) + sizeof(struct upload_chunk) + data.bytes > MAX_UPLOAD_SIZE) { AIM_LOG_ERROR("Attempted to upload more than %u bytes", MAX_UPLOAD_SIZE); indigo_cxn_send_error_reply( cxn_id, msg, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); cleanup_lua_upload(); return; } /* If the list isn't empty, get a pointer to the last uploaded chunk */ struct upload_chunk *prev = NULL; if (xbuf_length(&upload_chunks) > 0) { AIM_ASSERT(last_uploaded_chunk_offset < xbuf_length(&upload_chunks)); prev = xbuf_data(&upload_chunks) + last_uploaded_chunk_offset; } if (prev && !memcmp(filename, prev->filename, sizeof(filename))) { /* Concatenate consecutive messages with the same filename */ prev->size += data.bytes; xbuf_append(&upload_chunks, (char *)data.data, data.bytes); AIM_LOG_VERBOSE("Appended to Lua chunk %s, now %u bytes", prev->filename, prev->size); } else if (data.bytes > 0) { last_uploaded_chunk_offset = xbuf_length(&upload_chunks); struct upload_chunk *chunk = xbuf_reserve(&upload_chunks, sizeof(*chunk) + data.bytes); chunk->size = data.bytes; memcpy(chunk->filename, filename, sizeof(of_str64_t)); memcpy(chunk->data, data.data, data.bytes); AIM_LOG_VERBOSE("Uploaded Lua chunk %s, %u bytes", chunk->filename, chunk->size); } if (!(flags & OFP_BSN_LUA_UPLOAD_MORE)) { commit_lua_upload(cxn_id, msg); } }
void ind_core_bsn_table_set_buckets_size_handler(of_object_t *_obj, indigo_cxn_id_t cxn_id) { of_bsn_table_set_buckets_size_t *obj = _obj; uint16_t table_id; uint32_t buckets_size; indigo_error_t rv; of_bsn_table_set_buckets_size_table_id_get(obj, &table_id); of_bsn_table_set_buckets_size_buckets_size_get(obj, &buckets_size); rv = ft_set_checksum_buckets_size(ind_core_ft, table_id, buckets_size); if (rv < 0) { AIM_LOG_WARN("Failed to set table %d checksum buckets size to %d: %s", table_id, buckets_size, indigo_strerror(rv)); indigo_cxn_send_error_reply(cxn_id, obj, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); } }
static void handle_lua_command_request(indigo_cxn_id_t cxn_id, of_object_t *msg) { const int max_reply_size = UINT16_MAX - of_object_fixed_len[msg->version][OF_BSN_LUA_COMMAND_REPLY]; uint32_t xid; of_octets_t request_data; of_bsn_lua_command_request_xid_get(msg, &xid); of_bsn_lua_command_request_data_get(msg, &request_data); pipeline_lua_allocator_reset(); void *request_buf = pipeline_lua_allocator_dup(request_data.data, request_data.bytes); void *reply_buf = pipeline_lua_allocator_alloc(max_reply_size); lua_rawgeti(lua, LUA_REGISTRYINDEX, command_ref); lua_pushlightuserdata(lua, request_buf); lua_pushinteger(lua, request_data.bytes); lua_pushlightuserdata(lua, reply_buf); lua_pushinteger(lua, max_reply_size); if (lua_pcall(lua, 4, 1, 0) != 0) { AIM_LOG_ERROR("Failed to execute command xid=%#x: %s", xid, lua_tostring(lua, -1)); indigo_cxn_send_error_reply( cxn_id, msg, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); return; } int reply_size = lua_tointeger(lua, 0); AIM_TRUE_OR_DIE(reply_size >= 0 && reply_size < max_reply_size); lua_settop(lua, 0); of_object_t *reply = of_bsn_lua_command_reply_new(msg->version); of_bsn_lua_command_reply_xid_set(reply, xid); of_octets_t reply_data = { .data = reply_buf, .bytes = reply_size }; if (of_bsn_lua_command_reply_data_set(reply, &reply_data) < 0) { AIM_DIE("Unexpectedly failed to set data in of_bsn_lua_command_reply"); } indigo_cxn_send_controller_message(cxn_id, reply); }
static void commit_lua_upload(indigo_cxn_id_t cxn_id, of_object_t *msg) { uint16_t flags; of_bsn_lua_upload_flags_get(msg, &flags); /* TODO use stronger hash function */ uint32_t new_checksum = murmur_hash(xbuf_data(&upload_chunks), xbuf_length(&upload_chunks), 0); if (!(flags & OFP_BSN_LUA_UPLOAD_FORCE) && checksum == new_checksum) { AIM_LOG_VERBOSE("Skipping Lua commit, checksums match"); goto cleanup; } checksum = 0; reset_lua(); uint32_t offset = 0; while (offset < xbuf_length(&upload_chunks)) { struct upload_chunk *chunk = xbuf_data(&upload_chunks) + offset; offset += sizeof(*chunk) + chunk->size; AIM_LOG_VERBOSE("Loading Lua chunk %s, %u bytes", chunk->filename, chunk->size); char name[64]; snprintf(name, sizeof(name), "=%s", chunk->filename); if (luaL_loadbuffer(lua, chunk->data, chunk->size, name) != 0) { AIM_LOG_ERROR("Failed to load code: %s", lua_tostring(lua, -1)); indigo_cxn_send_error_reply( cxn_id, msg, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); goto cleanup; } /* Set the environment of the new chunk to the sandbox */ lua_getglobal(lua, "sandbox"); lua_setfenv(lua, -2); if (lua_pcall(lua, 0, 1, 0) != 0) { AIM_LOG_ERROR("Failed to execute code %s: %s", chunk->filename, lua_tostring(lua, -1)); indigo_cxn_send_error_reply( cxn_id, msg, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); goto cleanup; } /* Save the return value in the "modules" table, used by require */ char *module_name = aim_strdup(chunk->filename); char *dot = strrchr(module_name, '.'); if (dot) *dot = 0; /* strip file extension */ lua_getglobal(lua, "modules"); lua_pushstring(lua, module_name); lua_pushvalue(lua, -3); /* return value from pcall */ lua_rawset(lua, -3); /* modules[filename] = return_value */ lua_pop(lua, 2); /* pop modules and return value */ free(module_name); } checksum = new_checksum; cleanup: cleanup_lua_upload(); return; }
void ind_core_bsn_flow_checksum_bucket_stats_request_handler(of_object_t *_obj, indigo_cxn_id_t cxn_id) { of_bsn_flow_checksum_bucket_stats_request_t *obj = _obj; of_bsn_flow_checksum_bucket_stats_reply_t *reply; of_list_bsn_flow_checksum_bucket_stats_entry_t entries; of_bsn_flow_checksum_bucket_stats_entry_t *entry; uint32_t xid; uint8_t table_id; ft_table_t *table; int bucket_idx; of_bsn_flow_checksum_bucket_stats_request_table_id_get(obj, &table_id); if (table_id >= FT_MAX_TABLES) { AIM_LOG_WARN("Invalid table ID %u", table_id); indigo_cxn_send_error_reply(cxn_id, obj, OF_ERROR_TYPE_BAD_REQUEST, OF_REQUEST_FAILED_EPERM); return; } table = &ind_core_ft->tables[table_id]; reply = of_bsn_flow_checksum_bucket_stats_reply_new(obj->version); AIM_TRUE_OR_DIE(reply != NULL); of_bsn_flow_checksum_bucket_stats_request_xid_get(obj, &xid); of_bsn_flow_checksum_bucket_stats_reply_xid_set(reply, xid); of_bsn_flow_checksum_bucket_stats_reply_entries_bind(reply, &entries); entry = of_bsn_flow_checksum_bucket_stats_entry_new(entries.version); AIM_TRUE_OR_DIE(entry != NULL); for (bucket_idx = 0; bucket_idx < table->checksum_buckets_size; bucket_idx++) { of_bsn_flow_checksum_bucket_stats_entry_checksum_set( entry, table->checksum_buckets[bucket_idx]); if (of_list_append(&entries, entry) < 0) { /* This entry didn't fit, send out the current message and * allocate a new one. */ of_bsn_flow_checksum_bucket_stats_reply_flags_set( reply, OF_STATS_REPLY_FLAG_REPLY_MORE); indigo_cxn_send_controller_message(cxn_id, reply); reply = of_bsn_flow_checksum_bucket_stats_reply_new(obj->version); AIM_TRUE_OR_DIE(reply != NULL); of_bsn_flow_checksum_bucket_stats_reply_xid_set(reply, xid); of_bsn_flow_checksum_bucket_stats_reply_entries_bind(reply, &entries); if (of_list_append(&entries, entry) < 0) { AIM_DIE("unexpected failure appending single bsn_flow_checksum_bucket stats entry"); } } } of_object_delete(entry); indigo_cxn_send_controller_message(cxn_id, reply); }
void ind_cxn_bundle_ctrl_handle(connection_t *cxn, of_object_t *obj) { uint32_t bundle_id; uint16_t ctrl_type; uint16_t flags; uint16_t err_code = OFPBFC_UNKNOWN; of_bundle_ctrl_msg_bundle_id_get(obj, &bundle_id); of_bundle_ctrl_msg_bundle_ctrl_type_get(obj, &ctrl_type); of_bundle_ctrl_msg_flags_get(obj, &flags); bundle_t *bundle = find_bundle(cxn, bundle_id); if (ctrl_type == OFPBCT_OPEN_REQUEST) { if (bundle != NULL) { err_code = OFPBFC_BUNDLE_EXIST; goto error; } bundle = find_bundle(cxn, BUNDLE_ID_INVALID); if (bundle == NULL) { err_code = OFPBFC_OUT_OF_BUNDLES; goto error; } bundle->id = bundle_id; bundle->flags = flags; AIM_ASSERT(bundle->count == 0); AIM_ASSERT(bundle->allocated == 0); AIM_ASSERT(bundle->bytes == 0); AIM_ASSERT(bundle->msgs == NULL); } else if (ctrl_type == OFPBCT_CLOSE_REQUEST) { /* Ignored */ } else if (ctrl_type == OFPBCT_COMMIT_REQUEST) { if (bundle == NULL) { err_code = OFPBFC_BAD_ID; goto error; } if (comparator && !(bundle->flags & OFPBF_ORDERED)) { qsort(bundle->msgs, bundle->count, sizeof(bundle->msgs[0]), compare_message); } struct bundle_task_state *state = aim_zmalloc(sizeof(*state)); state->cxn_id = cxn->cxn_id; state->reply = of_object_dup(obj); of_bundle_ctrl_msg_bundle_ctrl_type_set(state->reply, OFPBCT_COMMIT_REPLY); state->id = bundle->id; state->count = bundle->count; state->offset = 0; state->msgs = bundle->msgs; if (ind_soc_task_register(bundle_task, state, IND_SOC_NORMAL_PRIORITY) < 0) { AIM_DIE("Failed to create long running task for bundle"); } ind_cxn_pause(cxn); /* Transfer ownership of msgs to task */ bundle->msgs = NULL; bundle->count = 0; free_bundle(bundle); /* Do not send reply yet */ return; } else if (ctrl_type == OFPBCT_DISCARD_REQUEST) { if (bundle == NULL) { err_code = OFPBFC_BAD_ID; goto error; } free_bundle(bundle); } else { err_code = OFPBFC_BAD_TYPE; goto error; } /* Send reply */ of_object_t *reply = of_object_dup(obj); /* Derive the reply subtype from the request */ of_bundle_ctrl_msg_bundle_ctrl_type_set(reply, ctrl_type+1); indigo_cxn_send_controller_message(cxn->cxn_id, reply); return; error: indigo_cxn_send_error_reply( cxn->cxn_id, obj, OF_ERROR_TYPE_BUNDLE_FAILED, err_code); }