blob: 6f20658b0e3e9210c9796eaeab41c691f6fa371b [file] [log] [blame]
Rich Lanea06d0c32013-03-25 08:52:03 -07001:: # Copyright 2013, Big Switch Networks, Inc.
2:: #
3:: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4:: # the following special exception:
5:: #
6:: # LOXI Exception
7:: #
8:: # As a special exception to the terms of the EPL, you may distribute libraries
9:: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10:: # that copyright and licensing notices generated by LoxiGen are not altered or removed
11:: # from the LoxiGen Libraries and the notice provided below is (i) included in
12:: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13:: # documentation for the LoxiGen Libraries, if distributed in binary form.
14:: #
15:: # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16:: #
17:: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18:: # a copy of the EPL at:
19:: #
20:: # http://www.eclipse.org/legal/epl-v10.html
21:: #
22:: # Unless required by applicable law or agreed to in writing, software
23:: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24:: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25:: # EPL for the specific language governing permissions and limitations
26:: # under the EPL.
27::
Rich Laned983aa52013-06-13 11:48:37 -070028:: include('_copyright.c')
Rich Lanea06d0c32013-03-25 08:52:03 -070029
30/****************************************************************
31 *
32 * of_object.c
33 *
34 * These are the low level object constructor/destructor operators.
35 *
36 ****************************************************************/
37
38#include "loci_log.h"
39#include <loci/loci.h>
40#include <loci/loci_validator.h>
41
Rich Lanea06d0c32013-03-25 08:52:03 -070042/**
43 * Create a generic new object and possibly underlying wire buffer
44 * @param bytes The number of bytes to allocate in the underlying buffer
45 *
46 * If bytes <= 0, do not allocate a wire buffer.
47 *
48 * Note that this is an internal function. The class specific
49 * new functions should be called to properly initialize and track an
50 * OF object.
51 */
52
53of_object_t *
54of_object_new(int bytes)
55{
56 of_object_t *obj;
57
Rich Lane671e7722013-12-15 16:48:54 -080058 if ((obj = (of_object_t *)MALLOC(sizeof(*obj))) == NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -070059 return NULL;
60 }
Rich Lane671e7722013-12-15 16:48:54 -080061 MEMSET(obj, 0, sizeof(*obj));
Rich Lanea06d0c32013-03-25 08:52:03 -070062
63 if (bytes > 0) {
64 if ((obj->wire_object.wbuf = of_wire_buffer_new(bytes)) == NULL) {
65 FREE(obj);
66 return NULL;
67 }
68 obj->wire_object.owned = 1;
69 }
70
71 return obj;
72}
73
74/**
75 * The delete function for LOCI objects
76 *
77 * @param obj Pointer to the object to be deleted
78 *
79 * This can be called on any LOCI object; it should not need to be
80 * overridden.
81 */
82
83void
84of_object_delete(of_object_t *obj)
85{
86 if (obj == NULL) {
87 return;
88 }
89
Rich Lanea06d0c32013-03-25 08:52:03 -070090 if (obj->wire_object.owned) {
91 of_wire_buffer_free(obj->wire_object.wbuf);
92 }
93
94 FREE(obj);
95}
96
97/**
98 * Duplicate an object
99 * @param src The object to be duplicated
100 * @returns Pointer to the duplicate or NULL on error. Caller is responsible
101 * for freeing the returned object.
102 */
103
104of_object_t *
Rich Lanecd6ef152013-12-15 16:42:18 -0800105of_object_dup(of_object_t *src)
Rich Lanea06d0c32013-03-25 08:52:03 -0700106{
107 of_object_t *dst;
108 of_object_init_f init_fn;
109
Rich Lane671e7722013-12-15 16:48:54 -0800110 if ((dst = (of_object_t *)MALLOC(sizeof(*dst))) == NULL) {
Rich Lanea06d0c32013-03-25 08:52:03 -0700111 return NULL;
112 }
113
114 MEMSET(dst, 0, sizeof(*dst));
115
116 /* Allocate a minimal wire buffer assuming we will not write to it. */
117 if ((dst->wire_object.wbuf = of_wire_buffer_new(src->length)) == NULL) {
118 FREE(dst);
119 return NULL;
120 }
121
122 dst->wire_object.owned = 1;
123
124 init_fn = of_object_init_map[src->object_id];
125 init_fn(dst, src->version, src->length, 0);
126
127 MEMCPY(OF_OBJECT_BUFFER_INDEX(dst, 0),
128 OF_OBJECT_BUFFER_INDEX(src, 0),
129 src->length);
130
131 return dst;
132}
133
Rich Lanea06d0c32013-03-25 08:52:03 -0700134/**
135 * Generic new from message call
136 */
137
138of_object_t *
139of_object_new_from_message(of_message_t msg, int len)
140{
141 of_object_id_t object_id;
142 of_object_t *obj;
143 of_version_t version;
144
145 version = of_message_version_get(msg);
146 if (!OF_VERSION_OKAY(version)) {
147 return NULL;
148 }
149
150 if (of_validate_message(msg, len) != 0) {
151 LOCI_LOG_ERROR("message validation failed\n");
152 return NULL;
153 }
154
Rich Lanea06d0c32013-03-25 08:52:03 -0700155 if ((obj = of_object_new(-1)) == NULL) {
156 return NULL;
157 }
158
Rich Lanea06d0c32013-03-25 08:52:03 -0700159 if (of_object_buffer_bind(obj, OF_MESSAGE_TO_BUFFER(msg), len,
160 OF_MESSAGE_FREE_FUNCTION) < 0) {
161 FREE(obj);
162 return NULL;
163 }
Rich Lanea06d0c32013-03-25 08:52:03 -0700164 obj->version = version;
165
Rich Lane76f181e2014-03-04 23:23:36 -0800166 of_header_wire_object_id_get(obj, &object_id);
167 of_object_init_map[object_id](obj, version, len, 0);
168
Rich Lanea06d0c32013-03-25 08:52:03 -0700169 return obj;
170}
171
172/**
Rich Lanec73680c2014-02-22 10:44:28 -0800173 * Parse a message without allocating memory
174 *
175 * @param storage Pointer to an uninitialized of_object_storage_t
176 * @param buf Pointer to the buffer
177 * @param length Length of buf
178 * @returns Pointer to an initialized of_object_t
179 *
180 * The lifetime of the returned object is the minimum of the lifetimes of
181 * 'buf' and 'storage'.
182 */
183
184of_object_t *
185of_object_new_from_message_preallocated(of_object_storage_t *storage,
186 uint8_t *buf, int len)
187{
188 of_object_t *obj = &storage->obj;
189 of_wire_buffer_t *wbuf = &storage->wbuf;
190 of_message_t msg = buf;
191 of_version_t version;
192 of_object_id_t object_id;
193
194 memset(storage, 0, sizeof(*storage));
195
196 version = of_message_version_get(msg);
197 if (!OF_VERSION_OKAY(version)) {
198 return NULL;
199 }
200
201 if (of_validate_message(msg, len) != 0) {
202 LOCI_LOG_ERROR("message validation failed\n");
203 return NULL;
204 }
205
Rich Lane76f181e2014-03-04 23:23:36 -0800206 obj->version = version;
Rich Lanec73680c2014-02-22 10:44:28 -0800207 obj->wire_object.wbuf = wbuf;
208 wbuf->buf = msg;
209 wbuf->alloc_bytes = len;
210 wbuf->current_bytes = len;
211
Rich Lane76f181e2014-03-04 23:23:36 -0800212 of_header_wire_object_id_get(obj, &object_id);
213 of_object_init_map[object_id](obj, version, len, 0);
214
Rich Lanec73680c2014-02-22 10:44:28 -0800215 return obj;
216}
217
218/**
Rich Lanea06d0c32013-03-25 08:52:03 -0700219 * Bind an existing buffer to an LOCI object
220 *
221 * @param obj Pointer to the object to be updated
222 * @param buf Pointer to the buffer to bind to obj
223 * @param bytes Length of buf
224 * @param buf_free An optional free function to be applied to
225 * buf on deallocation
226 *
227 * This can be called on any LOCI object; it should not need to be
228 * overridden.
229 */
230
231int
232of_object_buffer_bind(of_object_t *obj, uint8_t *buf, int bytes,
233 of_buffer_free_f buf_free)
234{
235 of_wire_object_t *wobj;
236 of_wire_buffer_t *wbuf;
237
Rich Lanee57f0432014-02-19 10:31:53 -0800238 LOCI_ASSERT(buf != NULL);
239 LOCI_ASSERT(bytes > 0);
240 // LOCI_ASSERT(wobj is not bound);
Rich Lanea06d0c32013-03-25 08:52:03 -0700241
242 wobj = &obj->wire_object;
243 MEMSET(wobj, 0, sizeof(*wobj));
244
245 wbuf = of_wire_buffer_new_bind(buf, bytes, buf_free);
246 if (wbuf == NULL) {
247 return OF_ERROR_RESOURCE;
248 }
249
250 wobj->wbuf = wbuf;
251 wobj->owned = 1;
252 obj->length = bytes;
253
254 return OF_ERROR_NONE;
255}
256
257/**
258 * Connect a child to a parent at the wire buffer level
259 *
260 * @param parent The top level object to bind to
261 * @param child The sub-object connecting to the parent
262 * @param offset The offset at which to attach the child RELATIVE
263 * TO THE PARENT in the buffer
264 * @param bytes The amount of the buffer dedicated to the child; see below
265 * @param inc_ref_count Should the ref count of the parent be incremented
266 *
267 * This is used for 'get' accessors for composite types as well as
268 * iterator functions for lists, both read (first/next) and write
269 * (append_init, append_advance).
270 *
271 * Connect a child object to a parent by setting up the child's
272 * wire_object to point to the parent's underlying buffer. The value
273 * of the parameter bytes is important in determining how the child
274 * is initialized:
275 * @li If bytes <= 0, the length and type of the child are not modified;
276 * no additional space is added to the buffer.
277 * @li If bytes > 0, the current wire buffer is grown to
278 * accomodate this many bytes. This is to support append operations.
279 *
280 * If an error is returned, future references to the child object
281 * (until it is reinitialized) are undefined.
282 */
283static void
284object_child_attach(of_object_t *parent, of_object_t *child,
285 int offset, int bytes)
286{
287 of_wire_object_t *c_wobj; /* Pointer to child's wire object */
288 of_wire_buffer_t *wbuf; /* Pointer to common wire buffer manager */
289
290 child->parent = parent;
291 wbuf = parent->wire_object.wbuf;
292
293 /* Set up the child's wire buf to point to same as parent */
294 c_wobj = &child->wire_object;
295 c_wobj->wbuf = wbuf;
296 c_wobj->obj_offset = parent->wire_object.obj_offset + offset;
297 c_wobj->owned = 0;
298
299 /*
300 * bytes determines if this is a read or write setup.
301 * If > 0, grow the buffer to accomodate the space
302 * Otherwise do nothing
303 */
304 if (bytes > 0) { /* Set internal length, request buffer space */
305 int tot_bytes; /* Total bytes to request for buffer if updated */
306
307 /* Set up space for the child in the parent's buffer */
308 tot_bytes = parent->wire_object.obj_offset + offset + bytes;
309
310 of_wire_buffer_grow(wbuf, tot_bytes);
311 child->length = bytes;
312 }
313 /* if bytes == 0 don't do anything */
314}
315
316/**
317 * Check for room in an object's wire buffer.
318 * @param obj The object being checked
319 * @param new_len The desired length
320 * @return Boolean
321 */
322
323int
324of_object_can_grow(of_object_t *obj, int new_len)
325{
326 return OF_OBJECT_ABSOLUTE_OFFSET(obj, new_len) <=
327 WBUF_ALLOC_BYTES(obj->wire_object.wbuf);
328}
329
330/**
331 * Set the xid of a message object
332 * @param obj The object being accessed
333 * @param xid The xid value to store in the wire buffer
334 * @return OF_ERROR_
335 * Since the XID is common across all versions, this is used
336 * for all XID accessors.
337 */
338
339int
340of_object_xid_set(of_object_t *obj, uint32_t xid)
341{
342 of_wire_buffer_t *wbuf;
343
344 if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) {
345 return OF_ERROR_PARAM;
346 }
347 of_wire_buffer_u32_set(wbuf,
348 OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid);
349 return OF_ERROR_NONE;
350}
351
352/**
353 * Get the xid of a message object
354 * @param obj The object being accessed
355 * @param xid Pointer to where to store the xid value
356 * @return OF_ERROR_
357 * Since the XID is common across all versions, this is used
358 * for all XID accessors.
359 */
360
361int
362of_object_xid_get(of_object_t *obj, uint32_t *xid)
363{
364 of_wire_buffer_t *wbuf;
365
366 if ((wbuf = OF_OBJECT_TO_WBUF(obj)) == NULL) {
367 return OF_ERROR_PARAM;
368 }
369 of_wire_buffer_u32_get(wbuf,
370 OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_MESSAGE_XID_OFFSET), xid);
371 return OF_ERROR_NONE;
372}
373
374/****************************************************************
375 *
376 * Generic list operation implementations
377 *
378 ****************************************************************/
379
380/**
381 * Set up a child for appending to a parent list
382 * @param parent The parent; must be a list object
383 * @param child The child object; must be of type list element
384 * @return OF_ERROR_
385 *
386 * Attaches the wire buffer of the parent to the child by pointing
387 * the child to the end of the parent.
388 *
389 * Set the wire length and type from the child.
390 * Update the parent length adding the current child length
391 *
392 * After calling this function, the child object may be updated
393 * resulting in changes to the parent's wire buffer
394 *
395 */
396
397int
398of_list_append_bind(of_object_t *parent, of_object_t *child)
399{
400 if (parent == NULL || child == NULL ||
401 parent->wire_object.wbuf == NULL) {
402 return OF_ERROR_PARAM;
403 }
404
405 if (!of_object_can_grow(parent, parent->length + child->length)) {
406 return OF_ERROR_RESOURCE;
407 }
408
409 object_child_attach(parent, child, parent->length,
410 child->length);
411
412 /* Update the wire length and type if needed */
Rich Lanedc46fe22014-04-03 15:10:38 -0700413 of_object_wire_length_set(child, child->length);
414 of_object_wire_type_set(child);
Rich Lanea06d0c32013-03-25 08:52:03 -0700415
416 /* Update the parent's length */
417 of_object_parent_length_update(parent, child->length);
418
419 OF_LENGTH_CHECK_ASSERT(parent);
420
421 return OF_ERROR_NONE;
422}
423
424/**
425 * Generic atomic list append operation
426 * @param list The list to which an item is being appended
427 * @param item THe item to append to the list
428 *
429 * The contents of the item are copied to the end of the list.
430 * Currently assumes the list is at the end of its parent.
431 */
432int
433of_list_append(of_object_t *list, of_object_t *item)
434{
435 int new_len;
436
437 new_len = list->length + item->length;
438
439 if (!of_object_can_grow(list, new_len)) {
440 return OF_ERROR_RESOURCE;
441 }
442
443 of_wire_buffer_grow(list->wire_object.wbuf,
444 OF_OBJECT_ABSOLUTE_OFFSET(list, new_len));
445
446 MEMCPY(OF_OBJECT_BUFFER_INDEX(list, list->length),
447 OF_OBJECT_BUFFER_INDEX(item, 0), item->length);
448
449 /* Update the list's length */
450 of_object_parent_length_update(list, item->length);
451
452 OF_LENGTH_CHECK_ASSERT(list);
453
454 return OF_ERROR_NONE;
455}
456
457/**
458 * Generic list first function
459 * @param parent The parent; must be a list object
460 * @param child The child object; must be of type list element
461 * @return OF_ERROR_RANGE if list is empty
462 * @return OF_ERROR_
463 *
464 * Sets up the child to point to the first element in the list
465 *
466 * Child init must be called before this is called.
467 *
468 * @note TREAT AS PRIVATE
469 * Does not fully initialized object
470 */
471int
472of_list_first(of_object_t *parent, of_object_t *child)
473{
474 if (parent->length == 0) { /* Empty list */
475 return OF_ERROR_RANGE;
476 }
477
478 object_child_attach(parent, child, 0, 0);
479
480 return OF_ERROR_NONE;
481}
482
483/**
484 * Return boolean indicating if child is pointing to last entry in parent
485 * @param parent The parent; must be a list object
486 * @param child The child object; must be of type list element
487 * @return OF_ERROR_RANGE if list is empty
488 * @return OF_ERROR_
489 *
490 */
491static int
492of_list_is_last(of_object_t *parent, of_object_t *child)
493{
494 if (child->wire_object.obj_offset + child->length >=
495 parent->wire_object.obj_offset + parent->length) {
496 return 1;
497 }
498
499 return 0;
500}
501
502/**
503 * Generic list next function
504 * @param parent The parent; must be a list object
505 * @param child The child object; must be of type list element
506 * @return OF_ERROR_RANGE if at end of list
507 * @return OF_ERROR_
508 *
509 * Advances the child to point to the subsequent element in the list.
510 * The wire buffer object must not have been modified since the
511 * previous call to _first or _next.
512 *
513 * @note TREAT AS PRIVATE
514 * Does not fully initialized object
515 */
516int
517of_list_next(of_object_t *parent, of_object_t *child)
518{
519 int offset;
520
Rich Lanee57f0432014-02-19 10:31:53 -0800521 LOCI_ASSERT(child->length > 0);
Rich Lanea06d0c32013-03-25 08:52:03 -0700522
523 /* Get offset of parent */
524 if (of_list_is_last(parent, child)) {
525 return OF_ERROR_RANGE; /* We were on the last object */
526 }
527
528 /* Offset is relative to parent start */
529 offset = (child->wire_object.obj_offset - parent->wire_object.obj_offset) +
530 child->length;
531 object_child_attach(parent, child, offset, 0);
532
533 return OF_ERROR_NONE;
534}
535
536void
537of_object_wire_buffer_steal(of_object_t *obj, uint8_t **buffer)
538{
Rich Lanee57f0432014-02-19 10:31:53 -0800539 LOCI_ASSERT(obj != NULL);
Rich Lanea06d0c32013-03-25 08:52:03 -0700540 of_wire_buffer_steal(obj->wire_object.wbuf, buffer);
541 obj->wire_object.wbuf = NULL;
542}
543
Rich Lane50aa5942013-12-15 16:20:38 -0800544#define _MAX_PARENT_ITERATIONS 4
545/**
546 * Iteratively update parent lengths thru hierarchy
547 * @param obj The object whose length is being updated
548 * @param delta The difference between the current and new lengths
549 *
550 * Note that this includes updating the object itself. It will
551 * iterate thru parents.
552 *
553 * Assumes delta > 0.
554 */
555void
556of_object_parent_length_update(of_object_t *obj, int delta)
557{
558#ifndef NDEBUG
559 int count = 0;
560 of_wire_buffer_t *wbuf; /* For debug asserts only */
561#endif
562
563 while (obj != NULL) {
Rich Lanee57f0432014-02-19 10:31:53 -0800564 LOCI_ASSERT(count++ < _MAX_PARENT_ITERATIONS);
Rich Lane50aa5942013-12-15 16:20:38 -0800565 obj->length += delta;
Rich Lanedc46fe22014-04-03 15:10:38 -0700566 of_object_wire_length_set(obj, obj->length);
Rich Lane50aa5942013-12-15 16:20:38 -0800567#ifndef NDEBUG
568 wbuf = obj->wire_object.wbuf;
569#endif
570
571 /* Asserts for wire length checking */
Rich Lanee57f0432014-02-19 10:31:53 -0800572 LOCI_ASSERT(obj->length + obj->wire_object.obj_offset <=
Rich Lane50aa5942013-12-15 16:20:38 -0800573 WBUF_CURRENT_BYTES(wbuf));
574 if (obj->parent == NULL) {
Rich Lanee57f0432014-02-19 10:31:53 -0800575 LOCI_ASSERT(obj->length + obj->wire_object.obj_offset ==
Rich Lane50aa5942013-12-15 16:20:38 -0800576 WBUF_CURRENT_BYTES(wbuf));
577 }
578
579 obj = obj->parent;
580 }
581}
582
Rich Lanec0e20ff2013-12-15 23:40:31 -0800583/**
584 * Use the type/length from the wire buffer and init the object
585 * @param obj The object being initialized
586 * @param base_object_id If > 0, this indicates the base object
587 * @param max_len If > 0, the max length to expect for the obj
588 * type for inheritance checking
589 * @return OF_ERROR_
590 *
591 * Used for inheritance type objects such as actions and OXMs
592 * The type is checked and if valid, the object is initialized.
593 * Then the length is taken from the buffer.
594 *
595 * Note that the object version must already be properly set.
596 */
597int
598of_object_wire_init(of_object_t *obj, of_object_id_t base_object_id,
599 int max_len)
600{
Rich Lanedc46fe22014-04-03 15:10:38 -0700601 if (loci_class_metadata[obj->object_id].wire_type_get != NULL) {
Rich Lanec0e20ff2013-12-15 23:40:31 -0800602 of_object_id_t id;
Rich Lanedc46fe22014-04-03 15:10:38 -0700603 loci_class_metadata[obj->object_id].wire_type_get(obj, &id);
Rich Lanec0e20ff2013-12-15 23:40:31 -0800604 if (!of_wire_id_valid(id, base_object_id)) {
605 return OF_ERROR_PARSE;
606 }
607 obj->object_id = id;
608 /* Call the init function for this object type; do not push to wire */
609 of_object_init_map[id]((of_object_t *)(obj), obj->version, -1, 0);
610 }
Rich Lanedc46fe22014-04-03 15:10:38 -0700611 if (loci_class_metadata[obj->object_id].wire_length_get != NULL) {
Rich Lanec0e20ff2013-12-15 23:40:31 -0800612 int length;
Rich Lanedc46fe22014-04-03 15:10:38 -0700613 loci_class_metadata[obj->object_id].wire_length_get(obj, &length);
Rich Lanec0e20ff2013-12-15 23:40:31 -0800614 if (length < 0 || (max_len > 0 && length > max_len)) {
615 return OF_ERROR_PARSE;
616 }
617 obj->length = length;
618 } else {
619 /* @fixme Does this cover everything else? */
620 obj->length = of_object_fixed_len[obj->version][base_object_id];
621 }
622
623 return OF_ERROR_NONE;
624}
625
Rich Lanea06d0c32013-03-25 08:52:03 -0700626/*
627 * Set member:
628 * get_wbuf_extent
629 * find offset of start of member
630 * if offset is at wbuf_extent (append new data)
631 * copy data at extent
632 * update parent length
633 * else
634 * find length of current entry
635 * move from end of current to extent to create (or remove) space
636 * copy data to offset
637 * update my length -- NEED LOCAL INFO TO DO THIS for some cases
638 */
639
640/* Also need: get offset of member for all combinations */
641/* Also need: get length of member for all combinations */
642#if 0
643/**
644 * Append the wire buffer data from src to the end of dst's wire buffer
645 */
646int
647of_object_append_buffer(of_object_t *dst, of_object_t *src)
648{
649 of_wire_buffer_t *s_wbuf, *d_wbuf;
650 int orig_len, dst_offset, src_offset;
651
652 d_wbuf = OF_OBJECT_TO_WBUF(dst);
653 s_wbuf = OF_OBJECT_TO_WBUF(src);
654 dst_offset = dst->wire_object.obj_offset + dst_length;
655 src_offset = src->wire_object.obj_offset;
656 OF_WIRE_BUFFER_INIT_CHECK(d_wbuf, dst_offset + src->length);
657 MEMCPY(OF_WBUF_BUFFER_POINTER(d_wbuf, dst_offset),
658 OF_WBUF_BUFFER_POINTER(s_wbuf, 0), src->length);
659
660 orig_len = dst->length;
661 dst->length += src->length;
662
663 return OF_ERROR_NONE;
664}
665
666/**
667 * Set the length of the actions object in a packet_in object
668 */
669
670int
671of_packet_out_actions_length_set(of_packet_t *obj, int len)
672{
673 if (obj == NULL || obj->object_id != OF_PACKET_IN ||
674 obj->wire_object.wbuf == NULL) {
675 return OF_ERROR_PARAM;
676 }
677
678 obj->actions_len_set(obj, len);
679}
680
681int
682_packet_out_data_offset_get(of_packet_t *obj)
683{
684 if (obj == NULL || obj->object_id != OF_PACKET_IN ||
685 obj->wire_object.wbuf == NULL) {
686 return -1;
687 }
688
689 return OF_PACKET_OUT_FIXED_LENGTH + _packet_out_actions_length_get(obj);
690}
691
692
693/**
694 * Simple length derivation function
695 *
696 * Most variable length fields are alone at the end of a structure.
697 * Their length is a simple calculation, just the total length of
698 * the parent minus the length of the non-variable part of the
699 * parent's class type.
700 *
701 * @param parent The parent object
702 * @param length (out) Where to store the length of the final
703 * variable length member
704 */
705int
706of_object_simple_length_derive(of_object_t *obj, int *length)
707{
708
709}
710#endif