]>
Commit | Line | Data |
---|---|---|
91088554 | 1 | /* |
cf62fa4c | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
91088554 DDP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
cf62fa4c PS |
17 | #ifndef DPBUF_H |
18 | #define DPBUF_H 1 | |
91088554 | 19 | |
cf62fa4c PS |
20 | #include <stddef.h> |
21 | #include <stdint.h> | |
22 | #include "list.h" | |
23 | #include "packets.h" | |
24 | #include "util.h" | |
25 | #include "netdev-dpdk.h" | |
91088554 DDP |
26 | |
27 | #ifdef __cplusplus | |
28 | extern "C" { | |
29 | #endif | |
30 | ||
cf62fa4c PS |
31 | enum OVS_PACKED_ENUM dp_packet_source { |
32 | DPBUF_MALLOC, /* Obtained via malloc(). */ | |
33 | DPBUF_STACK, /* Un-movable stack space or static buffer. */ | |
34 | DPBUF_STUB, /* Starts on stack, may expand into heap. */ | |
35 | DPBUF_DPDK, /* buffer data is from DPDK allocated memory. | |
36 | ref to build_dp_packet() in netdev-dpdk. */ | |
37 | }; | |
91088554 | 38 | |
cf62fa4c PS |
39 | /* Buffer for holding arbitrary data. An dp_packet is automatically reallocated |
40 | * as necessary if it grows too large for the available memory. | |
41 | * | |
42 | * 'frame' and offset conventions: | |
43 | * | |
44 | * Network frames (aka "packets"): 'frame' MUST be set to the start of the | |
45 | * packet, layer offsets MAY be set as appropriate for the packet. | |
46 | * Additionally, we assume in many places that the 'frame' and 'data' are | |
47 | * the same for packets. | |
48 | * | |
49 | * OpenFlow messages: 'frame' points to the start of the OpenFlow | |
50 | * header, while 'l3_ofs' is the length of the OpenFlow header. | |
51 | * When parsing, the 'data' will move past these, as data is being | |
52 | * pulled from the OpenFlow message. | |
53 | * | |
54 | * Actions: When encoding OVS action lists, the 'frame' is used | |
55 | * as a pointer to the beginning of the current action (see ofpact_put()). | |
56 | * | |
57 | * rconn: Reuses 'frame' as a private pointer while queuing. | |
58 | */ | |
e14deea0 | 59 | struct dp_packet { |
cf62fa4c PS |
60 | #ifdef DPDK_NETDEV |
61 | struct rte_mbuf mbuf; /* DPDK mbuf */ | |
62 | #else | |
63 | void *base_; /* First byte of allocated space. */ | |
64 | void *data_; /* First byte actually in use. */ | |
65 | uint32_t size_; /* Number of bytes in use. */ | |
8cbf4f47 | 66 | uint32_t dp_hash; /* Packet hash. */ |
61a2647e | 67 | #endif |
cf62fa4c PS |
68 | uint32_t allocated; /* Number of bytes allocated. */ |
69 | ||
70 | void *frame; /* Packet frame start, or NULL. */ | |
71 | enum dp_packet_source source; /* Source of memory allocated as 'base'. */ | |
72 | uint8_t l2_pad_size; /* Detected l2 padding size. | |
73 | * Padding is non-pullable. */ | |
74 | uint16_t l2_5_ofs; /* MPLS label stack offset from 'frame', or | |
75 | * UINT16_MAX */ | |
76 | uint16_t l3_ofs; /* Network-level header offset from 'frame', | |
77 | or UINT16_MAX. */ | |
78 | uint16_t l4_ofs; /* Transport-level header offset from 'frame', | |
79 | or UINT16_MAX. */ | |
41ccaa24 | 80 | struct pkt_metadata md; |
cf62fa4c | 81 | struct ovs_list list_node; /* Private list element for use by owner. */ |
91088554 DDP |
82 | }; |
83 | ||
cf62fa4c PS |
84 | static inline void * dp_packet_data(const struct dp_packet *); |
85 | static inline void dp_packet_set_data(struct dp_packet *, void *); | |
86 | static inline void * dp_packet_base(const struct dp_packet *); | |
87 | static inline void dp_packet_set_base(struct dp_packet *, void *); | |
88 | ||
89 | static inline uint32_t dp_packet_size(const struct dp_packet *); | |
90 | static inline void dp_packet_set_size(struct dp_packet *, uint32_t); | |
91 | ||
92 | void * dp_packet_resize_l2(struct dp_packet *, int increment); | |
93 | void * dp_packet_resize_l2_5(struct dp_packet *, int increment); | |
94 | static inline void * dp_packet_l2(const struct dp_packet *); | |
95 | static inline void dp_packet_set_frame(struct dp_packet *, void *); | |
96 | static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *); | |
97 | static inline void dp_packet_set_l2_pad_size(struct dp_packet *, uint8_t); | |
98 | static inline void * dp_packet_l2_5(const struct dp_packet *); | |
99 | static inline void dp_packet_set_l2_5(struct dp_packet *, void *); | |
100 | static inline void * dp_packet_l3(const struct dp_packet *); | |
101 | static inline void dp_packet_set_l3(struct dp_packet *, void *); | |
102 | static inline void * dp_packet_l4(const struct dp_packet *); | |
103 | static inline void dp_packet_set_l4(struct dp_packet *, void *); | |
104 | static inline size_t dp_packet_l4_size(const struct dp_packet *); | |
105 | static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *); | |
106 | static inline const void *dp_packet_get_udp_payload(const struct dp_packet *); | |
107 | static inline const void *dp_packet_get_sctp_payload(const struct dp_packet *); | |
108 | static inline const void *dp_packet_get_icmp_payload(const struct dp_packet *); | |
109 | static inline const void *dp_packet_get_nd_payload(const struct dp_packet *); | |
110 | ||
111 | void dp_packet_use(struct dp_packet *, void *, size_t); | |
112 | void dp_packet_use_stub(struct dp_packet *, void *, size_t); | |
113 | void dp_packet_use_const(struct dp_packet *, const void *, size_t); | |
114 | ||
115 | void dp_packet_init_dpdk(struct dp_packet *b, size_t allocated); | |
116 | ||
117 | void dp_packet_init(struct dp_packet *, size_t); | |
118 | void dp_packet_uninit(struct dp_packet *); | |
119 | static inline void *dp_packet_get_uninit_pointer(struct dp_packet *); | |
120 | ||
121 | struct dp_packet *dp_packet_new(size_t); | |
122 | struct dp_packet *dp_packet_new_with_headroom(size_t, size_t headroom); | |
123 | struct dp_packet *dp_packet_clone(const struct dp_packet *); | |
124 | struct dp_packet *dp_packet_clone_with_headroom(const struct dp_packet *, | |
125 | size_t headroom); | |
126 | struct dp_packet *dp_packet_clone_data(const void *, size_t); | |
127 | struct dp_packet *dp_packet_clone_data_with_headroom(const void *, size_t, | |
128 | size_t headroom); | |
129 | static inline void dp_packet_delete(struct dp_packet *); | |
130 | ||
131 | static inline void *dp_packet_at(const struct dp_packet *, size_t offset, | |
132 | size_t size); | |
133 | static inline void *dp_packet_at_assert(const struct dp_packet *, size_t offset, | |
134 | size_t size); | |
135 | static inline void *dp_packet_tail(const struct dp_packet *); | |
136 | static inline void *dp_packet_end(const struct dp_packet *); | |
137 | ||
138 | void *dp_packet_put_uninit(struct dp_packet *, size_t); | |
139 | void *dp_packet_put_zeros(struct dp_packet *, size_t); | |
140 | void *dp_packet_put(struct dp_packet *, const void *, size_t); | |
141 | char *dp_packet_put_hex(struct dp_packet *, const char *s, size_t *n); | |
142 | void dp_packet_reserve(struct dp_packet *, size_t); | |
143 | void dp_packet_reserve_with_tailroom(struct dp_packet *b, size_t headroom, | |
144 | size_t tailroom); | |
145 | void *dp_packet_push_uninit(struct dp_packet *b, size_t); | |
146 | void *dp_packet_push_zeros(struct dp_packet *, size_t); | |
147 | void *dp_packet_push(struct dp_packet *b, const void *, size_t); | |
148 | ||
149 | static inline size_t dp_packet_headroom(const struct dp_packet *); | |
150 | static inline size_t dp_packet_tailroom(const struct dp_packet *); | |
151 | void dp_packet_prealloc_headroom(struct dp_packet *, size_t); | |
152 | void dp_packet_prealloc_tailroom(struct dp_packet *, size_t); | |
153 | void dp_packet_shift(struct dp_packet *, int); | |
91088554 | 154 | |
cf62fa4c PS |
155 | static inline void dp_packet_clear(struct dp_packet *); |
156 | static inline void *dp_packet_pull(struct dp_packet *, size_t); | |
157 | static inline void *dp_packet_try_pull(struct dp_packet *, size_t); | |
91088554 | 158 | |
cf62fa4c | 159 | void *dp_packet_steal_data(struct dp_packet *); |
91088554 | 160 | |
cf62fa4c PS |
161 | char *dp_packet_to_string(const struct dp_packet *, size_t maxbytes); |
162 | static inline struct dp_packet *dp_packet_from_list(const struct ovs_list *); | |
163 | void dp_packet_list_delete(struct ovs_list *); | |
164 | static inline bool dp_packet_equal(const struct dp_packet *, const struct dp_packet *); | |
165 | ||
166 | \f | |
167 | /* Returns a pointer that may be passed to free() to accomplish the same thing | |
168 | * as dp_packet_uninit(b). The return value is a null pointer if dp_packet_uninit() | |
169 | * would not free any memory. */ | |
170 | static inline void *dp_packet_get_uninit_pointer(struct dp_packet *b) | |
171 | { | |
172 | /* XXX: If 'source' is DPBUF_DPDK memory gets leaked! */ | |
173 | return b && b->source == DPBUF_MALLOC ? dp_packet_base(b) : NULL; | |
174 | } | |
175 | ||
176 | /* Frees memory that 'b' points to, as well as 'b' itself. */ | |
177 | static inline void dp_packet_delete(struct dp_packet *b) | |
178 | { | |
179 | if (b) { | |
180 | if (b->source == DPBUF_DPDK) { | |
181 | /* If this dp_packet was allocated by DPDK it must have been | |
182 | * created as a dp_packet */ | |
183 | free_dpdk_buf((struct dp_packet*) b); | |
184 | return; | |
185 | } | |
186 | ||
187 | dp_packet_uninit(b); | |
188 | free(b); | |
189 | } | |
190 | } | |
191 | ||
192 | /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to | |
193 | * byte 'offset'. Otherwise, returns a null pointer. */ | |
194 | static inline void *dp_packet_at(const struct dp_packet *b, size_t offset, | |
195 | size_t size) | |
91088554 | 196 | { |
cf62fa4c PS |
197 | return offset + size <= dp_packet_size(b) ? (char *) dp_packet_data(b) + offset : NULL; |
198 | } | |
91088554 | 199 | |
cf62fa4c PS |
200 | /* Returns a pointer to byte 'offset' in 'b', which must contain at least |
201 | * 'offset + size' bytes of data. */ | |
202 | static inline void *dp_packet_at_assert(const struct dp_packet *b, size_t offset, | |
203 | size_t size) | |
204 | { | |
205 | ovs_assert(offset + size <= dp_packet_size(b)); | |
206 | return ((char *) dp_packet_data(b)) + offset; | |
207 | } | |
208 | ||
209 | /* Returns a pointer to byte following the last byte of data in use in 'b'. */ | |
210 | static inline void *dp_packet_tail(const struct dp_packet *b) | |
211 | { | |
212 | return (char *) dp_packet_data(b) + dp_packet_size(b); | |
213 | } | |
214 | ||
215 | /* Returns a pointer to byte following the last byte allocated for use (but | |
216 | * not necessarily in use) in 'b'. */ | |
217 | static inline void *dp_packet_end(const struct dp_packet *b) | |
218 | { | |
219 | return (char *) dp_packet_base(b) + b->allocated; | |
220 | } | |
221 | ||
222 | /* Returns the number of bytes of headroom in 'b', that is, the number of bytes | |
223 | * of unused space in dp_packet 'b' before the data that is in use. (Most | |
224 | * commonly, the data in a dp_packet is at its beginning, and thus the dp_packet's | |
225 | * headroom is 0.) */ | |
226 | static inline size_t dp_packet_headroom(const struct dp_packet *b) | |
227 | { | |
228 | return (char*)dp_packet_data(b) - (char*)dp_packet_base(b); | |
229 | } | |
230 | ||
231 | /* Returns the number of bytes that may be appended to the tail end of dp_packet | |
232 | * 'b' before the dp_packet must be reallocated. */ | |
233 | static inline size_t dp_packet_tailroom(const struct dp_packet *b) | |
234 | { | |
235 | return (char*)dp_packet_end(b) - (char*)dp_packet_tail(b); | |
236 | } | |
237 | ||
238 | /* Clears any data from 'b'. */ | |
239 | static inline void dp_packet_clear(struct dp_packet *b) | |
240 | { | |
241 | dp_packet_set_data(b, dp_packet_base(b)); | |
242 | dp_packet_set_size(b, 0); | |
243 | } | |
244 | ||
245 | /* Removes 'size' bytes from the head end of 'b', which must contain at least | |
246 | * 'size' bytes of data. Returns the first byte of data removed. */ | |
247 | static inline void *dp_packet_pull(struct dp_packet *b, size_t size) | |
248 | { | |
249 | void *data = dp_packet_data(b); | |
250 | ovs_assert(dp_packet_size(b) - dp_packet_l2_pad_size(b) >= size); | |
251 | dp_packet_set_data(b, (char*)dp_packet_data(b) + size); | |
252 | dp_packet_set_size(b, dp_packet_size(b) - size); | |
253 | return data; | |
254 | } | |
255 | ||
256 | /* If 'b' has at least 'size' bytes of data, removes that many bytes from the | |
257 | * head end of 'b' and returns the first byte removed. Otherwise, returns a | |
258 | * null pointer without modifying 'b'. */ | |
259 | static inline void *dp_packet_try_pull(struct dp_packet *b, size_t size) | |
260 | { | |
261 | return dp_packet_size(b) - dp_packet_l2_pad_size(b) >= size | |
262 | ? dp_packet_pull(b, size) : NULL; | |
263 | } | |
264 | ||
265 | static inline struct dp_packet *dp_packet_from_list(const struct ovs_list *list) | |
266 | { | |
267 | return CONTAINER_OF(list, struct dp_packet, list_node); | |
268 | } | |
269 | ||
270 | static inline bool dp_packet_equal(const struct dp_packet *a, const struct dp_packet *b) | |
271 | { | |
272 | return dp_packet_size(a) == dp_packet_size(b) && | |
273 | memcmp(dp_packet_data(a), dp_packet_data(b), dp_packet_size(a)) == 0; | |
274 | } | |
275 | ||
276 | /* Get the start if the Ethernet frame. 'l3_ofs' marks the end of the l2 | |
277 | * headers, so return NULL if it is not set. */ | |
278 | static inline void * dp_packet_l2(const struct dp_packet *b) | |
279 | { | |
280 | return (b->l3_ofs != UINT16_MAX) ? b->frame : NULL; | |
281 | } | |
282 | ||
283 | /* Sets the packet frame start pointer and resets all layer offsets. | |
284 | * l3 offset must be set before 'l2' can be retrieved. */ | |
285 | static inline void dp_packet_set_frame(struct dp_packet *b, void *packet) | |
286 | { | |
287 | b->frame = packet; | |
288 | b->l2_pad_size = 0; | |
289 | b->l2_5_ofs = UINT16_MAX; | |
290 | b->l3_ofs = UINT16_MAX; | |
291 | b->l4_ofs = UINT16_MAX; | |
292 | } | |
293 | ||
294 | static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *b) | |
295 | { | |
296 | return b->l2_pad_size; | |
297 | } | |
298 | ||
299 | static inline void dp_packet_set_l2_pad_size(struct dp_packet *b, uint8_t pad_size) | |
300 | { | |
301 | ovs_assert(pad_size <= dp_packet_size(b)); | |
302 | b->l2_pad_size = pad_size; | |
303 | } | |
304 | ||
305 | static inline void * dp_packet_l2_5(const struct dp_packet *b) | |
306 | { | |
307 | return b->l2_5_ofs != UINT16_MAX ? (char *)b->frame + b->l2_5_ofs : NULL; | |
308 | } | |
309 | ||
310 | static inline void dp_packet_set_l2_5(struct dp_packet *b, void *l2_5) | |
311 | { | |
312 | b->l2_5_ofs = l2_5 ? (char *)l2_5 - (char *)b->frame : UINT16_MAX; | |
313 | } | |
314 | ||
315 | static inline void * dp_packet_l3(const struct dp_packet *b) | |
316 | { | |
317 | return b->l3_ofs != UINT16_MAX ? (char *)b->frame + b->l3_ofs : NULL; | |
318 | } | |
319 | ||
320 | static inline void dp_packet_set_l3(struct dp_packet *b, void *l3) | |
321 | { | |
322 | b->l3_ofs = l3 ? (char *)l3 - (char *)b->frame : UINT16_MAX; | |
323 | } | |
324 | ||
325 | static inline void * dp_packet_l4(const struct dp_packet *b) | |
326 | { | |
327 | return b->l4_ofs != UINT16_MAX ? (char *)b->frame + b->l4_ofs : NULL; | |
328 | } | |
329 | ||
330 | static inline void dp_packet_set_l4(struct dp_packet *b, void *l4) | |
331 | { | |
332 | b->l4_ofs = l4 ? (char *)l4 - (char *)b->frame : UINT16_MAX; | |
333 | } | |
334 | ||
335 | static inline size_t dp_packet_l4_size(const struct dp_packet *b) | |
336 | { | |
337 | return b->l4_ofs != UINT16_MAX | |
338 | ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b) | |
339 | - dp_packet_l2_pad_size(b) | |
340 | : 0; | |
341 | } | |
342 | ||
343 | static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *b) | |
344 | { | |
345 | size_t l4_size = dp_packet_l4_size(b); | |
346 | ||
347 | if (OVS_LIKELY(l4_size >= TCP_HEADER_LEN)) { | |
348 | struct tcp_header *tcp = dp_packet_l4(b); | |
349 | int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4; | |
350 | ||
351 | if (OVS_LIKELY(tcp_len >= TCP_HEADER_LEN && tcp_len <= l4_size)) { | |
352 | return (const char *)tcp + tcp_len; | |
353 | } | |
354 | } | |
355 | return NULL; | |
356 | } | |
357 | ||
358 | static inline const void *dp_packet_get_udp_payload(const struct dp_packet *b) | |
359 | { | |
360 | return OVS_LIKELY(dp_packet_l4_size(b) >= UDP_HEADER_LEN) | |
361 | ? (const char *)dp_packet_l4(b) + UDP_HEADER_LEN : NULL; | |
362 | } | |
363 | ||
364 | static inline const void *dp_packet_get_sctp_payload(const struct dp_packet *b) | |
365 | { | |
366 | return OVS_LIKELY(dp_packet_l4_size(b) >= SCTP_HEADER_LEN) | |
367 | ? (const char *)dp_packet_l4(b) + SCTP_HEADER_LEN : NULL; | |
368 | } | |
369 | ||
370 | static inline const void *dp_packet_get_icmp_payload(const struct dp_packet *b) | |
371 | { | |
372 | return OVS_LIKELY(dp_packet_l4_size(b) >= ICMP_HEADER_LEN) | |
373 | ? (const char *)dp_packet_l4(b) + ICMP_HEADER_LEN : NULL; | |
374 | } | |
375 | ||
376 | static inline const void *dp_packet_get_nd_payload(const struct dp_packet *b) | |
377 | { | |
378 | return OVS_LIKELY(dp_packet_l4_size(b) >= ND_MSG_LEN) | |
379 | ? (const char *)dp_packet_l4(b) + ND_MSG_LEN : NULL; | |
380 | } | |
381 | ||
382 | #ifdef DPDK_NETDEV | |
383 | BUILD_ASSERT_DECL(offsetof(struct dp_packet, mbuf) == 0); | |
384 | ||
385 | static inline void * dp_packet_data(const struct dp_packet *b) | |
386 | { | |
387 | return b->mbuf.pkt.data; | |
388 | } | |
389 | ||
390 | static inline void dp_packet_set_data(struct dp_packet *b, void *d) | |
391 | { | |
392 | b->mbuf.pkt.data = d; | |
393 | } | |
394 | ||
395 | static inline void * dp_packet_base(const struct dp_packet *b) | |
396 | { | |
397 | return b->mbuf.buf_addr; | |
398 | } | |
399 | ||
400 | static inline void dp_packet_set_base(struct dp_packet *b, void *d) | |
401 | { | |
402 | b->mbuf.buf_addr = d; | |
403 | } | |
404 | ||
405 | static inline uint32_t dp_packet_size(const struct dp_packet *b) | |
406 | { | |
407 | return b->mbuf.pkt.pkt_len; | |
408 | } | |
409 | ||
410 | static inline void dp_packet_set_size(struct dp_packet *b, uint32_t v) | |
411 | { | |
412 | b->mbuf.pkt.data_len = v; /* Current seg length. */ | |
413 | b->mbuf.pkt.pkt_len = v; /* Total length of all segments linked to | |
414 | * this segment. */ | |
415 | } | |
416 | ||
417 | #else | |
418 | static inline void * dp_packet_data(const struct dp_packet *b) | |
419 | { | |
420 | return b->data_; | |
421 | } | |
422 | ||
423 | static inline void dp_packet_set_data(struct dp_packet *b, void *d) | |
424 | { | |
425 | b->data_ = d; | |
426 | } | |
427 | ||
428 | static inline void * dp_packet_base(const struct dp_packet *b) | |
429 | { | |
430 | return b->base_; | |
431 | } | |
432 | ||
433 | static inline void dp_packet_set_base(struct dp_packet *b, void *d) | |
434 | { | |
435 | b->base_ = d; | |
436 | } | |
437 | ||
438 | static inline uint32_t dp_packet_size(const struct dp_packet *b) | |
439 | { | |
440 | return b->size_; | |
441 | } | |
442 | ||
443 | static inline void dp_packet_set_size(struct dp_packet *b, uint32_t v) | |
444 | { | |
445 | b->size_ = v; | |
446 | } | |
447 | #endif | |
448 | ||
449 | static inline void dp_packet_reset_packet(struct dp_packet *b, int off) | |
450 | { | |
451 | dp_packet_set_size(b, dp_packet_size(b) - off); | |
452 | dp_packet_set_data(b, (void *) ((unsigned char *) b->frame + off)); | |
453 | b->frame = NULL; | |
454 | b->l2_5_ofs = b->l3_ofs = b->l4_ofs = UINT16_MAX; | |
91088554 DDP |
455 | } |
456 | ||
e14deea0 | 457 | static inline uint32_t dp_packet_get_dp_hash(struct dp_packet *p) |
61a2647e DDP |
458 | { |
459 | #ifdef DPDK_NETDEV | |
cf62fa4c | 460 | return p->mbuf.pkt.hash.rss; |
61a2647e DDP |
461 | #else |
462 | return p->dp_hash; | |
463 | #endif | |
464 | } | |
465 | ||
e14deea0 | 466 | static inline void dp_packet_set_dp_hash(struct dp_packet *p, |
61a2647e DDP |
467 | uint32_t hash) |
468 | { | |
469 | #ifdef DPDK_NETDEV | |
cf62fa4c | 470 | p->mbuf.pkt.hash.rss = hash; |
61a2647e DDP |
471 | #else |
472 | p->dp_hash = hash; | |
473 | #endif | |
474 | } | |
475 | ||
91088554 DDP |
476 | #ifdef __cplusplus |
477 | } | |
478 | #endif | |
479 | ||
cf62fa4c | 480 | #endif /* dp_packet.h */ |