| :: # Copyright 2013, Big Switch Networks, Inc. |
| :: # |
| :: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with |
| :: # the following special exception: |
| :: # |
| :: # LOXI Exception |
| :: # |
| :: # As a special exception to the terms of the EPL, you may distribute libraries |
| :: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided |
| :: # that copyright and licensing notices generated by LoxiGen are not altered or removed |
| :: # from the LoxiGen Libraries and the notice provided below is (i) included in |
| :: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any |
| :: # documentation for the LoxiGen Libraries, if distributed in binary form. |
| :: # |
| :: # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler." |
| :: # |
| :: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain |
| :: # a copy of the EPL at: |
| :: # |
| :: # http://www.eclipse.org/legal/epl-v10.html |
| :: # |
| :: # Unless required by applicable law or agreed to in writing, software |
| :: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| :: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| :: # EPL for the specific language governing permissions and limitations |
| :: # under the EPL. |
| :: |
| :: include('_copyright.c') |
| |
| /**************************************************************** |
| * |
| * of_object.c |
| * |
| * These are the low level object constructor/destructor operators. |
| * |
| ****************************************************************/ |
| |
| #include "loci_log.h" |
| #include <loci/loci.h> |
| #include <loci/loci_validator.h> |
| |
| /** |
| * Create a generic new object and possibly underlying wire buffer |
| * @param bytes The number of bytes to allocate in the underlying buffer |
| * |
| * If bytes <= 0, do not allocate a wire buffer. |
| * |
| * Note that this is an internal function. The class specific |
| * new functions should be called to properly initialize and track an |
| * OF object. |
| */ |
| |
| of_object_t * |
| of_object_new(int bytes) |
| { |
| of_object_t *obj; |
| |
| if ((obj = (of_object_t *)MALLOC(sizeof(*obj))) == NULL) { |
| return NULL; |
| } |
| MEMSET(obj, 0, sizeof(*obj)); |
| |
| if (bytes > 0) { |
| if ((obj->wbuf = of_wire_buffer_new(bytes)) == NULL) { |
| FREE(obj); |
| return NULL; |
| } |
| } |
| |
| return obj; |
| } |
| |
| /** |
| * The delete function for LOCI objects |
| * |
| * @param obj Pointer to the object to be deleted |
| * |
| * This can be called on any LOCI object; it should not need to be |
| * overridden. |
| */ |
| |
| void |
| of_object_delete(of_object_t *obj) |
| { |
| if (obj == NULL) { |
| return; |
| } |
| |
| if (obj->parent == NULL) { |
| of_wire_buffer_free(obj->wbuf); |
| } |
| |
| FREE(obj); |
| } |
| |
| /** |
| * Duplicate an object |
| * @param src The object to be duplicated |
| * @returns Pointer to the duplicate or NULL on error. Caller is responsible |
| * for freeing the returned object. |
| */ |
| |
| of_object_t * |
| of_object_dup(of_object_t *src) |
| { |
| of_object_t *dst; |
| of_object_init_f init_fn; |
| |
| if ((dst = (of_object_t *)MALLOC(sizeof(*dst))) == NULL) { |
| return NULL; |
| } |
| |
| MEMSET(dst, 0, sizeof(*dst)); |
| |
| /* Allocate a minimal wire buffer assuming we will not write to it. */ |
| if ((dst->wbuf = of_wire_buffer_new(src->length)) == NULL) { |
| FREE(dst); |
| return NULL; |
| } |
| |
| init_fn = of_object_init_map[src->object_id]; |
| init_fn(dst, src->version, src->length, 0); |
| |
| MEMCPY(OF_OBJECT_BUFFER_INDEX(dst, 0), |
| OF_OBJECT_BUFFER_INDEX(src, 0), |
| src->length); |
| |
| return dst; |
| } |
| |
| /** |
| * Generic new from message call |
| */ |
| |
| of_object_t * |
| of_object_new_from_message(of_message_t msg, int len) |
| { |
| of_object_id_t object_id; |
| of_object_t *obj; |
| of_version_t version; |
| |
| version = of_message_version_get(msg); |
| if (!OF_VERSION_OKAY(version)) { |
| return NULL; |
| } |
| |
| if (of_validate_message(msg, len) != 0) { |
| LOCI_LOG_ERROR("message validation failed\n"); |
| return NULL; |
| } |
| |
| if ((obj = of_object_new(-1)) == NULL) { |
| return NULL; |
| } |
| |
| if (of_object_buffer_bind(obj, OF_MESSAGE_TO_BUFFER(msg), len, |
| OF_MESSAGE_FREE_FUNCTION) < 0) { |
| FREE(obj); |
| return NULL; |
| } |
| obj->version = version; |
| |
| of_header_wire_object_id_get(obj, &object_id); |
| of_object_init_map[object_id](obj, version, len, 0); |
| |
| return obj; |
| } |
| |
| /** |
| * Parse a message without allocating memory |
| * |
| * @param storage Pointer to an uninitialized of_object_storage_t |
| * @param buf Pointer to the buffer |
| * @param length Length of buf |
| * @returns Pointer to an initialized of_object_t |
| * |
| * The lifetime of the returned object is the minimum of the lifetimes of |
| * 'buf' and 'storage'. |
| */ |
| |
| of_object_t * |
| of_object_new_from_message_preallocated(of_object_storage_t *storage, |
| uint8_t *buf, int len) |
| { |
| of_object_t *obj = &storage->obj; |
| of_wire_buffer_t *wbuf = &storage->wbuf; |
| of_message_t msg = buf; |
| of_version_t version; |
| of_object_id_t object_id; |
| |
| memset(storage, 0, sizeof(*storage)); |
| |
| version = of_message_version_get(msg); |
| if (!OF_VERSION_OKAY(version)) { |
| return NULL; |
| } |
| |
| if (of_validate_message(msg, len) != 0) { |
| LOCI_LOG_ERROR("message validation failed\n"); |
| return NULL; |
| } |
| |
| obj->version = version; |
| obj->wbuf = wbuf; |
| wbuf->buf = msg; |
| wbuf->alloc_bytes = len; |
| wbuf->current_bytes = len; |
| |
| of_header_wire_object_id_get(obj, &object_id); |
| of_object_init_map[object_id](obj, version, len, 0); |
| |
| return obj; |
| } |
| |
| /** |
| * Bind an existing buffer to an LOCI object |
| * |
| * @param obj Pointer to the object to be updated |
| * @param buf Pointer to the buffer to bind to obj |
| * @param bytes Length of buf |
| * @param buf_free An optional free function to be applied to |
| * buf on deallocation |
| * |
| * This can be called on any LOCI object; it should not need to be |
| * overridden. |
| */ |
| |
| int |
| of_object_buffer_bind(of_object_t *obj, uint8_t *buf, int bytes, |
| of_buffer_free_f buf_free) |
| { |
| of_wire_buffer_t *wbuf; |
| |
| LOCI_ASSERT(buf != NULL); |
| LOCI_ASSERT(bytes > 0); |
| |
| wbuf = of_wire_buffer_new_bind(buf, bytes, buf_free); |
| if (wbuf == NULL) { |
| return OF_ERROR_RESOURCE; |
| } |
| |
| obj->wbuf = wbuf; |
| obj->obj_offset = 0; |
| obj->length = bytes; |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Connect a child to a parent at the wire buffer level |
| * |
| * @param parent The top level object to bind to |
| * @param child The sub-object connecting to the parent |
| * @param offset The offset at which to attach the child RELATIVE |
| * TO THE PARENT in the buffer |
| * @param bytes The amount of the buffer dedicated to the child; see below |
| * |
| * This is used for 'get' accessors for composite types as well as |
| * iterator functions for lists, both read (first/next) and write |
| * (append_init, append_advance). |
| * |
| * Connect a child object to a parent by setting up the child's |
| * wire_object to point to the parent's underlying buffer. The value |
| * of the parameter bytes is important in determining how the child |
| * is initialized: |
| * @li If bytes <= 0, the length and type of the child are not modified; |
| * no additional space is added to the buffer. |
| * @li If bytes > 0, the current wire buffer is grown to |
| * accomodate this many bytes. This is to support append operations. |
| * |
| * If an error is returned, future references to the child object |
| * (until it is reinitialized) are undefined. |
| */ |
| static void |
| object_child_attach(of_object_t *parent, of_object_t *child, |
| int offset, int bytes) |
| { |
| of_object_attach(parent, child, offset, bytes); |
| |
| /* |
| * bytes determines if this is a read or write setup. |
| * If > 0, grow the buffer to accomodate the space |
| * Otherwise do nothing |
| */ |
| if (bytes > 0) { /* Set internal length, request buffer space */ |
| int tot_bytes; /* Total bytes to request for buffer if updated */ |
| |
| /* Set up space for the child in the parent's buffer */ |
| tot_bytes = parent->obj_offset + offset + bytes; |
| |
| of_wire_buffer_grow(parent->wbuf, tot_bytes); |
| } |
| /* if bytes == 0 don't do anything */ |
| } |
| |
| /** |
| * Check for room in an object's wire buffer. |
| * @param obj The object being checked |
| * @param new_len The desired length |
| * @return Boolean |
| */ |
| |
| int |
| of_object_can_grow(of_object_t *obj, int new_len) |
| { |
| return OF_OBJECT_ABSOLUTE_OFFSET(obj, new_len) <= |
| WBUF_ALLOC_BYTES(obj->wbuf); |
| } |
| |
| /** |
| * Set the xid of a message object |
| * @param obj The object being accessed |
| * @param xid The xid value to store in the wire buffer |
| * @return OF_ERROR_ |
| * Since the XID is common across all versions, this is used |
| * for all XID accessors. |
| */ |
| |
| int |
| of_object_xid_set(of_object_t *obj, uint32_t xid) |
| { |
| of_wire_buffer_t *wbuf; |
| |
| if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) { |
| return OF_ERROR_PARAM; |
| } |
| of_wire_buffer_u32_set(wbuf, |
| OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid); |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Get the xid of a message object |
| * @param obj The object being accessed |
| * @param xid Pointer to where to store the xid value |
| * @return OF_ERROR_ |
| * Since the XID is common across all versions, this is used |
| * for all XID accessors. |
| */ |
| |
| int |
| of_object_xid_get(of_object_t *obj, uint32_t *xid) |
| { |
| of_wire_buffer_t *wbuf; |
| |
| if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) { |
| return OF_ERROR_PARAM; |
| } |
| of_wire_buffer_u32_get(wbuf, |
| OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid); |
| return OF_ERROR_NONE; |
| } |
| |
| /**************************************************************** |
| * |
| * Generic list operation implementations |
| * |
| ****************************************************************/ |
| |
| /** |
| * Set up a child for appending to a parent list |
| * @param parent The parent; must be a list object |
| * @param child The child object; must be of type list element |
| * @return OF_ERROR_ |
| * |
| * Attaches the wire buffer of the parent to the child by pointing |
| * the child to the end of the parent. |
| * |
| * Set the wire length and type from the child. |
| * Update the parent length adding the current child length |
| * |
| * After calling this function, the child object may be updated |
| * resulting in changes to the parent's wire buffer |
| * |
| */ |
| |
| int |
| of_list_append_bind(of_object_t *parent, of_object_t *child) |
| { |
| if (parent == NULL || child == NULL || |
| parent->wbuf == NULL) { |
| return OF_ERROR_PARAM; |
| } |
| |
| if (!of_object_can_grow(parent, parent->length + child->length)) { |
| return OF_ERROR_RESOURCE; |
| } |
| |
| object_child_attach(parent, child, parent->length, |
| child->length); |
| |
| /* Update the wire length and type if needed */ |
| of_object_wire_length_set(child, child->length); |
| of_object_wire_type_set(child); |
| |
| /* Update the parent's length */ |
| of_object_parent_length_update(parent, child->length); |
| |
| OF_LENGTH_CHECK_ASSERT(parent); |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Generic atomic list append operation |
| * @param list The list to which an item is being appended |
| * @param item THe item to append to the list |
| * |
| * The contents of the item are copied to the end of the list. |
| * Currently assumes the list is at the end of its parent. |
| */ |
| int |
| of_list_append(of_object_t *list, of_object_t *item) |
| { |
| int new_len; |
| |
| new_len = list->length + item->length; |
| |
| if (!of_object_can_grow(list, new_len)) { |
| return OF_ERROR_RESOURCE; |
| } |
| |
| of_wire_buffer_grow(list->wbuf, |
| OF_OBJECT_ABSOLUTE_OFFSET(list, new_len)); |
| |
| MEMCPY(OF_OBJECT_BUFFER_INDEX(list, list->length), |
| OF_OBJECT_BUFFER_INDEX(item, 0), item->length); |
| |
| /* Update the list's length */ |
| of_object_parent_length_update(list, item->length); |
| |
| OF_LENGTH_CHECK_ASSERT(list); |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Generic list first function |
| * @param parent The parent; must be a list object |
| * @param child The child object; must be of type list element |
| * @return OF_ERROR_RANGE if list is empty |
| * @return OF_ERROR_ |
| * |
| * Sets up the child to point to the first element in the list |
| * |
| * Child init must be called before this is called. |
| * |
| * @note TREAT AS PRIVATE |
| * Does not fully initialized object |
| */ |
| int |
| of_list_first(of_object_t *parent, of_object_t *child) |
| { |
| if (parent->length == 0) { /* Empty list */ |
| return OF_ERROR_RANGE; |
| } |
| |
| of_object_attach(parent, child, 0, child->length); |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Return boolean indicating if child is pointing to last entry in parent |
| * @param parent The parent; must be a list object |
| * @param child The child object; must be of type list element |
| * @return OF_ERROR_RANGE if list is empty |
| * @return OF_ERROR_ |
| * |
| */ |
| static int |
| of_list_is_last(of_object_t *parent, of_object_t *child) |
| { |
| if (child->obj_offset + child->length >= |
| parent->obj_offset + parent->length) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Generic list next function |
| * @param parent The parent; must be a list object |
| * @param child The child object; must be of type list element |
| * @return OF_ERROR_RANGE if at end of list |
| * @return OF_ERROR_ |
| * |
| * Advances the child to point to the subsequent element in the list. |
| * The wire buffer object must not have been modified since the |
| * previous call to _first or _next. |
| * |
| * @note TREAT AS PRIVATE |
| * Does not fully initialized object |
| */ |
| int |
| of_list_next(of_object_t *parent, of_object_t *child) |
| { |
| int offset; |
| |
| LOCI_ASSERT(child->length > 0); |
| |
| /* Get offset of parent */ |
| if (of_list_is_last(parent, child)) { |
| return OF_ERROR_RANGE; /* We were on the last object */ |
| } |
| |
| /* Offset is relative to parent start */ |
| offset = (child->obj_offset - parent->obj_offset) + |
| child->length; |
| of_object_attach(parent, child, offset, child->length); |
| |
| return OF_ERROR_NONE; |
| } |
| |
| void |
| of_object_wire_buffer_steal(of_object_t *obj, uint8_t **buffer) |
| { |
| LOCI_ASSERT(obj != NULL); |
| of_wire_buffer_steal(obj->wbuf, buffer); |
| obj->wbuf = NULL; |
| } |
| |
| #define _MAX_PARENT_ITERATIONS 4 |
| /** |
| * Iteratively update parent lengths thru hierarchy |
| * @param obj The object whose length is being updated |
| * @param delta The difference between the current and new lengths |
| * |
| * Note that this includes updating the object itself. It will |
| * iterate thru parents. |
| * |
| * Assumes delta > 0. |
| */ |
| void |
| of_object_parent_length_update(of_object_t *obj, int delta) |
| { |
| #ifndef NDEBUG |
| int count = 0; |
| of_wire_buffer_t *wbuf; /* For debug asserts only */ |
| #endif |
| |
| while (obj != NULL) { |
| LOCI_ASSERT(count++ < _MAX_PARENT_ITERATIONS); |
| obj->length += delta; |
| of_object_wire_length_set(obj, obj->length); |
| #ifndef NDEBUG |
| wbuf = obj->wbuf; |
| #endif |
| |
| /* Asserts for wire length checking */ |
| LOCI_ASSERT(obj->length + obj->obj_offset <= |
| WBUF_CURRENT_BYTES(wbuf)); |
| if (obj->parent == NULL) { |
| LOCI_ASSERT(obj->length + obj->obj_offset == |
| WBUF_CURRENT_BYTES(wbuf)); |
| } |
| |
| obj = obj->parent; |
| } |
| } |
| |
| /** |
| * Use the type/length from the wire buffer and init the object |
| * @param obj The object being initialized |
| * @param base_object_id If > 0, this indicates the base object |
| * @param max_len If > 0, the max length to expect for the obj |
| * type for inheritance checking |
| * @return OF_ERROR_ |
| * |
| * Used for inheritance type objects such as actions and OXMs |
| * The type is checked and if valid, the object is initialized. |
| * Then the length is taken from the buffer. |
| * |
| * Note that the object version must already be properly set. |
| */ |
| int |
| of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id, |
| int max_len) |
| { |
| if (loci_class_metadata[obj->object_id].wire_type_get != NULL) { |
| of_object_id_t id; |
| loci_class_metadata[obj->object_id].wire_type_get(obj, &id); |
| if (!of_wire_id_valid(id, base_object_id)) { |
| return OF_ERROR_PARSE; |
| } |
| obj->object_id = id; |
| /* Call the init function for this object type; do not push to wire */ |
| of_object_init_map[id]((of_object_t *)(obj), obj->version, -1, 0); |
| } |
| if (loci_class_metadata[obj->object_id].wire_length_get != NULL) { |
| int length; |
| loci_class_metadata[obj->object_id].wire_length_get(obj, &length); |
| if (length < 0 || (max_len > 0 && length > max_len)) { |
| return OF_ERROR_PARSE; |
| } |
| obj->length = length; |
| } else { |
| /* @fixme Does this cover everything else? */ |
| obj->length = of_object_fixed_len[obj->version][base_object_id]; |
| } |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /* |
| * Set member: |
| * get_wbuf_extent |
| * find offset of start of member |
| * if offset is at wbuf_extent (append new data) |
| * copy data at extent |
| * update parent length |
| * else |
| * find length of current entry |
| * move from end of current to extent to create (or remove) space |
| * copy data to offset |
| * update my length -- NEED LOCAL INFO TO DO THIS for some cases |
| */ |
| |
| /* Also need: get offset of member for all combinations */ |
| /* Also need: get length of member for all combinations */ |
| #if 0 |
| /** |
| * Append the wire buffer data from src to the end of dst's wire buffer |
| */ |
| int |
| of_object_append_buffer(of_object_t *dst, of_object_t *src) |
| { |
| of_wire_buffer_t *s_wbuf, *d_wbuf; |
| int orig_len, dst_offset, src_offset; |
| |
| d_wbuf = OF_OBJECT_TO_WBUF(dst); |
| s_wbuf = OF_OBJECT_TO_WBUF(src); |
| dst_offset = dst->obj_offset + dst_length; |
| src_offset = src->obj_offset; |
| OF_WIRE_BUFFER_INIT_CHECK(d_wbuf, dst_offset + src->length); |
| MEMCPY(OF_WBUF_BUFFER_POINTER(d_wbuf, dst_offset), |
| OF_WBUF_BUFFER_POINTER(s_wbuf, 0), src->length); |
| |
| orig_len = dst->length; |
| dst->length += src->length; |
| |
| return OF_ERROR_NONE; |
| } |
| |
| /** |
| * Set the length of the actions object in a packet_in object |
| */ |
| |
| int |
| of_packet_out_actions_length_set(of_packet_t *obj, int len) |
| { |
| if (obj == NULL || obj->object_id != OF_PACKET_IN || |
| obj->wbuf == NULL) { |
| return OF_ERROR_PARAM; |
| } |
| |
| obj->actions_len_set(obj, len); |
| } |
| |
| int |
| _packet_out_data_offset_get(of_packet_t *obj) |
| { |
| if (obj == NULL || obj->object_id != OF_PACKET_IN || |
| obj->wbuf == NULL) { |
| return -1; |
| } |
| |
| return OF_PACKET_OUT_FIXED_LENGTH + _packet_out_actions_length_get(obj); |
| } |
| |
| |
| /** |
| * Simple length derivation function |
| * |
| * Most variable length fields are alone at the end of a structure. |
| * Their length is a simple calculation, just the total length of |
| * the parent minus the length of the non-variable part of the |
| * parent's class type. |
| * |
| * @param parent The parent object |
| * @param length (out) Where to store the length of the final |
| * variable length member |
| */ |
| int |
| of_object_simple_length_derive(of_object_t *obj, int *length) |
| { |
| |
| } |
| #endif |