2 * Routines for XIP dissection
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
10 * The eXpressive Internet Protocol (XIP) is the network layer protocol for
11 * the eXpressive Internet Architecture (XIA), a future Internet architecture
12 * project. The addresses in XIP are directed acyclic graphs, so some of the
13 * code in this file verifies the correctness of the DAGs and displays them
14 * in human-readable form.
16 * More information about XIA can be found here:
17 * https://www.cs.cmu.edu/~xia/
20 * https://github.com/AltraMayor/XIA-for-Linux/wiki
22 * More information about the format of the DAG can be found here:
23 * https://github.com/AltraMayor/XIA-for-Linux/wiki/Human-readable-XIP-address-format
27 #include <epan/packet.h>
28 #include <epan/expert.h>
30 void proto_register_xip(void);
31 void proto_reg_handoff_xip(void);
33 /* Next dissector handles. */
34 static dissector_handle_t xip_serval_handle
;
38 static int hf_xip_version
;
39 static int hf_xip_next_hdr
;
40 static int hf_xip_payload_len
;
41 static int hf_xip_hop_limit
;
42 static int hf_xip_num_dst
;
43 static int hf_xip_num_src
;
44 static int hf_xip_last_node
;
45 static int hf_xip_dst_dag
;
46 static int hf_xip_dst_dag_entry
;
47 static int hf_xip_src_dag
;
48 static int hf_xip_src_dag_entry
;
50 static int ett_xip_tree
;
51 static int ett_xip_ddag
;
52 static int ett_xip_sdag
;
54 static expert_field ei_xip_invalid_len
;
55 static expert_field ei_xip_next_header
;
56 static expert_field ei_xip_bad_num_dst
;
57 static expert_field ei_xip_bad_num_src
;
59 static dissector_handle_t xip_handle
;
62 #define XIDTYPE_NAT 0x00
63 #define XIDTYPE_AD 0x10
64 #define XIDTYPE_HID 0x11
65 #define XIDTYPE_CID 0x12
66 #define XIDTYPE_SID 0x13
67 #define XIDTYPE_UNI4ID 0x14
68 #define XIDTYPE_I4ID 0x15
69 #define XIDTYPE_U4ID 0x16
70 #define XIDTYPE_XDP 0x17
71 #define XIDTYPE_SRVCID 0x18
72 #define XIDTYPE_FLOWID 0x19
73 #define XIDTYPE_ZF 0x20
75 /* Principal string values. */
76 static const value_string xidtype_vals
[] = {
78 { XIDTYPE_HID
, "hid" },
79 { XIDTYPE_CID
, "cid" },
80 { XIDTYPE_SID
, "sid" },
81 { XIDTYPE_UNI4ID
, "uni4id" },
82 { XIDTYPE_I4ID
, "i4id" },
83 { XIDTYPE_U4ID
, "u4id" },
84 { XIDTYPE_XDP
, "xdp" },
85 { XIDTYPE_SRVCID
, "serval" },
86 { XIDTYPE_FLOWID
, "flowid" },
92 /* There's a non-XIDTYPE_NAT node after an XIDTYPE_NAT node. */
93 XIAEADDR_NAT_MISPLACED
= 1,
94 /* Edge-selected bit is only valid in packets. */
96 /* There's a non-empty edge after an Empty Edge.
97 * This error can also occur if an empty edge is selected. */
98 XIAEADDR_EE_MISPLACED
,
99 /* An edge of a node is out of range. */
100 XIAEADDR_EDGE_OUT_RANGE
,
101 /* The nodes are not in topological order. Notice that being in
102 * topological guarantees that the graph is acyclic, and has a simple,
104 XIAEADDR_NOT_TOPOLOGICAL
,
105 /* No single component. */
106 XIAEADDR_MULTI_COMPONENTS
,
107 /* Entry node is not present. */
111 /* Maximum number of nodes in a DAG. */
112 #define XIA_NODES_MAX 9
114 /* Number of outgoing edges for each node. */
115 #define XIA_OUTDEGREE_MAX 4
117 /* Sizes of an XIA node and its components. */
118 #define XIA_TYPE_SIZE 4
119 #define XIA_XID_SIZE 20
120 #define XIA_EDGES_SIZE 4
121 #define XIA_NODE_SIZE (XIA_TYPE_SIZE + XIA_XID_SIZE + XIA_EDGES_SIZE)
123 /* Split XID up into 4 byte chunks. */
124 #define XIA_XID_CHUNK_SIZE 4
126 typedef uint32_t xid_type_t
;
132 /* XID, represented as 4 byte ints. */
133 uint32_t xid_id
[XIA_XID_SIZE
/ XIA_XID_CHUNK_SIZE
];
137 struct xia_xid s_xid
;
138 /* Outgoing edges. */
140 uint8_t a
[XIA_OUTDEGREE_MAX
];
146 struct xia_row s_row
[XIA_NODES_MAX
];
149 /* XIA_MAX_STRADDR_SIZE - The maximum size of an XIA address as a string
150 * in bytes. It's the recommended size to call xia_ntop with. It includes space
151 * for an invalid sign (i.e. '!'), the type and name of a nodes in
152 * hexadecimal, the out-edges, the two separators (i.e. '-') per node,
153 * the edge-chosen sign (i.e. '>') for each selected edge,
154 * the node separators (i.e. ':' or ":\n"), a string terminator (i.e. '\0'),
155 * and an extra '\n' at the end the caller may want to add.
157 #define MAX_PPAL_NAME_SIZE 32
158 #define XIA_MAX_STRID_SIZE (XIA_XID_SIZE * 2 + 1)
159 #define XIA_MAX_STRXID_SIZE (MAX_PPAL_NAME_SIZE + XIA_MAX_STRID_SIZE)
160 #define XIA_MAX_STRADDR_SIZE (1 + XIA_NODES_MAX * \
161 (XIA_MAX_STRXID_SIZE + XIA_OUTDEGREE_MAX * 2 + 2) + 1)
164 * Validating addresses
167 #define XIA_CHOSEN_EDGE 0x80
168 #define XIA_EMPTY_EDGE 0x7f
169 #define XIA_ENTRY_NODE_INDEX 0x7e
171 #define XIA_EMPTY_EDGES (XIA_EMPTY_EDGE << 24 | XIA_EMPTY_EDGE << 16 |\
172 XIA_EMPTY_EDGE << 8 | XIA_EMPTY_EDGE)
173 #define XIA_CHOSEN_EDGES (XIA_CHOSEN_EDGE << 24 | XIA_CHOSEN_EDGE << 16 |\
174 XIA_CHOSEN_EDGE << 8 | XIA_CHOSEN_EDGE)
177 is_edge_chosen(uint8_t e
)
179 return e
& XIA_CHOSEN_EDGE
;
183 is_any_edge_chosen(const struct xia_row
*row
)
185 return row
->s_edge
.i
& XIA_CHOSEN_EDGES
;
189 is_empty_edge(uint8_t e
)
191 return (e
& XIA_EMPTY_EDGE
) == XIA_EMPTY_EDGE
;
195 xia_is_nat(xid_type_t ty
)
197 return ty
== XIDTYPE_NAT
;
201 xia_are_edges_valid(const struct xia_row
*row
,
202 uint8_t node
, uint8_t num_node
, uint32_t *pvisited
)
205 uint32_t all_edges
, bits
;
208 if (is_any_edge_chosen(row
)) {
209 /* Since at least an edge of last_node has already
210 * been chosen, the address is corrupted.
212 return -XIAEADDR_CHOSEN_EDGE
;
215 edge
= row
->s_edge
.a
;
216 all_edges
= g_ntohl(row
->s_edge
.i
);
218 for (i
= 0; i
< XIA_OUTDEGREE_MAX
; i
++, edge
++) {
221 if (e
== XIA_EMPTY_EDGE
) {
222 if ((all_edges
& bits
) !=
223 (XIA_EMPTY_EDGES
& bits
))
224 return -XIAEADDR_EE_MISPLACED
;
227 } else if (e
>= num_node
) {
228 return -XIAEADDR_EDGE_OUT_RANGE
;
229 } else if (node
< (num_node
- 1) && e
<= node
) {
230 /* Notice that if (node == XIA_ENTRY_NODE_INDEX)
231 * it still works fine because XIA_ENTRY_NODE_INDEX
232 * is greater than (num_node - 1).
234 return -XIAEADDR_NOT_TOPOLOGICAL
;
243 xia_test_addr(const struct xia_addr
*addr
)
247 uint32_t visited
= 0;
249 /* Test that XIDTYPE_NAT is present only on last rows. */
251 for (i
= 0; i
< XIA_NODES_MAX
; i
++) {
253 ty
= addr
->s_row
[i
].s_xid
.xid_type
;
256 return -XIAEADDR_NAT_MISPLACED
;
257 } else if (xia_is_nat(ty
)) {
262 /* n = number of nodes from here. */
264 /* Test edges are well formed. */
265 for (i
= 0; i
< n
; i
++) {
267 rc
= xia_are_edges_valid(&addr
->s_row
[i
], i
, n
, &visited
);
273 /* Test entry point is present. Notice that it's just a
274 * friendlier error since it's also XIAEADDR_MULTI_COMPONENTS.
277 all_edges
= addr
->s_row
[n
- 1].s_edge
.i
;
278 if (all_edges
== XIA_EMPTY_EDGES
)
279 return -XIAEADDR_NO_ENTRY
;
281 if (visited
!= ((1U << n
) - 1))
282 return -XIAEADDR_MULTI_COMPONENTS
;
289 * Printing addresses out
292 #define INDEX_BASE 36
295 edge_to_char(uint8_t e
)
297 const char *ch_edge
= "0123456789abcdefghijklmnopqrstuvwxyz";
298 e
&= ~XIA_CHOSEN_EDGE
;
301 else if (is_empty_edge(e
))
308 add_edges_to_buf(int valid
, wmem_strbuf_t
*buf
, const uint8_t *edges
)
311 wmem_strbuf_append_c(buf
, '-');
312 for (i
= 0; i
< XIA_OUTDEGREE_MAX
; i
++) {
313 if (valid
&& edges
[i
] == XIA_EMPTY_EDGE
)
316 if (is_edge_chosen(edges
[i
]))
317 wmem_strbuf_append_c(buf
, '>');
319 wmem_strbuf_append_c(buf
, edge_to_char(edges
[i
]));
324 add_type_to_buf(xid_type_t ty
, wmem_strbuf_t
*buf
)
326 const char *xid_name
;
327 size_t buflen
= wmem_strbuf_get_len(buf
);
329 if (XIA_MAX_STRADDR_SIZE
- buflen
- 1 < MAX_PPAL_NAME_SIZE
)
332 xid_name
= try_val_to_str(ty
, xidtype_vals
);
334 wmem_strbuf_append_printf(buf
, "%s-", xid_name
);
336 wmem_strbuf_append_printf(buf
, "0x%x-", ty
);
340 add_id_to_buf(const struct xia_xid
*src
, wmem_strbuf_t
*buf
)
342 wmem_strbuf_append_printf(buf
, "%08x%08x%08x%08x%08x",
350 /* xia_ntop - convert an XIA address to a string.
351 * @src can be ill-formed, but xia_ntop won't report an error and will return
352 * a string that approximates that ill-formed address.
355 xia_ntop(const struct xia_addr
*src
, wmem_strbuf_t
*buf
)
359 valid
= xia_test_addr(src
) >= 1;
361 wmem_strbuf_append_c(buf
, '!');
363 for (i
= 0; i
< XIA_NODES_MAX
; i
++) {
364 const struct xia_row
*row
= &src
->s_row
[i
];
366 if (xia_is_nat(row
->s_xid
.xid_type
))
370 wmem_strbuf_append(buf
, ":\n");
372 /* Add the type, ID, and edges for this node. */
373 add_type_to_buf(row
->s_xid
.xid_type
, buf
);
374 add_id_to_buf(&row
->s_xid
, buf
);
375 add_edges_to_buf(valid
, buf
, row
->s_edge
.a
);
385 #define XIPH_MIN_LEN 36
386 #define ETHERTYPE_XIP 0xC0DE
387 #define XIA_NEXT_HEADER_DATA 0
389 /* Offsets of XIP fields in bytes. */
400 construct_dag(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*xip_tree
,
401 const int ett
, const int hf
, const int hf_entry
,
402 const uint8_t num_nodes
, int offset
)
404 proto_tree
*dag_tree
;
410 int dag_offset
= offset
;
412 ti
= proto_tree_add_item(xip_tree
, hf
, tvb
, offset
,
413 num_nodes
* XIA_NODE_SIZE
, ENC_BIG_ENDIAN
);
415 buf
= wmem_strbuf_new_sized(pinfo
->pool
, XIA_MAX_STRADDR_SIZE
);
417 dag_tree
= proto_item_add_subtree(ti
, ett
);
419 memset(&dag
, 0, sizeof(dag
));
420 for (i
= 0; i
< num_nodes
; i
++) {
421 struct xia_row
*row
= &dag
.s_row
[i
];
423 row
->s_xid
.xid_type
= tvb_get_ntohl(tvb
, offset
);
424 offset
+= XIA_TYPE_SIZE
;
426 /* Process the ID 32 bits at a time. */
427 for (j
= 0; j
< XIA_XID_SIZE
/ XIA_XID_CHUNK_SIZE
; j
++) {
428 row
->s_xid
.xid_id
[j
] = tvb_get_ntohl(tvb
, offset
);
429 offset
+= XIA_XID_CHUNK_SIZE
;
432 /* Need to process the edges byte-by-byte,
433 * so keep the bytes in network order.
435 tvb_memcpy(tvb
, row
->s_edge
.a
, offset
, XIA_EDGES_SIZE
);
436 offset
+= XIA_EDGES_SIZE
;
440 dag_str
= wmem_strbuf_get_str(buf
);
441 proto_tree_add_string_format(dag_tree
, hf_entry
, tvb
, dag_offset
,
442 XIA_NODE_SIZE
* num_nodes
, dag_str
, "%s", dag_str
);
446 dissect_xip_sink_node(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
447 int offset
, uint8_t sink_node
)
452 /* Serval XID types. */
455 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
456 return call_dissector(xip_serval_handle
, next_tvb
, pinfo
, tree
);
457 /* No special sink processing. */
464 dissect_xip_next_header(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
465 proto_item
*next_ti
, int offset
)
468 uint8_t next_header
= tvb_get_uint8(tvb
, XIPH_NXTH
);
470 switch (next_header
) {
471 case XIA_NEXT_HEADER_DATA
:
472 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
473 return call_data_dissector(next_tvb
, pinfo
, tree
);
475 expert_add_info_format(pinfo
, next_ti
, &ei_xip_next_header
,
476 "Unrecognized next header type: 0x%02x", next_header
);
482 display_xip(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
484 proto_tree
*xip_tree
= NULL
;
486 proto_item
*ti
= NULL
;
487 proto_item
*payload_ti
= NULL
;
488 proto_item
*next_ti
= NULL
;
489 proto_item
*num_ti
= NULL
;
492 uint16_t xiph_len
, payload_len
;
493 uint8_t num_dst_nodes
, num_src_nodes
, last_node
;
495 num_dst_nodes
= tvb_get_uint8(tvb
, XIPH_NDST
);
496 num_src_nodes
= tvb_get_uint8(tvb
, XIPH_NSRC
);
497 xiph_len
= 8 + (XIA_NODE_SIZE
* num_dst_nodes
) +
498 (XIA_NODE_SIZE
* num_src_nodes
);
500 /* Construct protocol tree. */
501 ti
= proto_tree_add_item(tree
, proto_xip
, tvb
, 0, xiph_len
, ENC_NA
);
502 xip_tree
= proto_item_add_subtree(ti
, ett_xip_tree
);
504 /* Add XIP version. */
505 proto_tree_add_item(xip_tree
, hf_xip_version
, tvb
,
506 XIPH_VERS
, 1, ENC_BIG_ENDIAN
);
508 /* Add XIP next header. */
509 next_ti
= proto_tree_add_item(xip_tree
, hf_xip_next_hdr
, tvb
,
510 XIPH_NXTH
, 1, ENC_BIG_ENDIAN
);
512 /* Add XIP payload length. */
513 payload_len
= tvb_get_ntohs(tvb
, XIPH_PLEN
);
514 payload_ti
= proto_tree_add_uint_format(xip_tree
, hf_xip_payload_len
,
515 tvb
, XIPH_PLEN
, 2, payload_len
, "Payload Length: %u bytes",
517 if (tvb_captured_length_remaining(tvb
, xiph_len
) != payload_len
)
518 expert_add_info_format(pinfo
, payload_ti
, &ei_xip_invalid_len
,
519 "Payload length field (%d bytes) does not match actual payload length (%d bytes)",
520 payload_len
, tvb_captured_length_remaining(tvb
, xiph_len
));
522 /* Add XIP hop limit. */
523 proto_tree_add_item(xip_tree
, hf_xip_hop_limit
, tvb
,
524 XIPH_HOPL
, 1, ENC_BIG_ENDIAN
);
526 /* Add XIP number of destination DAG nodes. */
527 num_ti
= proto_tree_add_item(xip_tree
, hf_xip_num_dst
, tvb
,
528 XIPH_NDST
, 1, ENC_BIG_ENDIAN
);
529 if (num_dst_nodes
> XIA_NODES_MAX
) {
530 expert_add_info_format(pinfo
, num_ti
, &ei_xip_bad_num_dst
,
531 "The number of destination DAG nodes (%d) must be less than XIA_NODES_MAX (%d)",
532 num_dst_nodes
, XIA_NODES_MAX
);
533 num_dst_nodes
= XIA_NODES_MAX
;
536 /* Add XIP number of source DAG nodes. */
537 num_ti
= proto_tree_add_item(xip_tree
, hf_xip_num_src
, tvb
,
538 XIPH_NSRC
, 1, ENC_BIG_ENDIAN
);
539 if (num_src_nodes
> XIA_NODES_MAX
) {
540 expert_add_info_format(pinfo
, num_ti
, &ei_xip_bad_num_src
,
541 "The number of source DAG nodes (%d) must be less than XIA_NODES_MAX (%d)",
542 num_src_nodes
, XIA_NODES_MAX
);
543 num_src_nodes
= XIA_NODES_MAX
;
546 /* Add XIP last node. */
547 last_node
= tvb_get_uint8(tvb
, XIPH_LSTN
);
548 proto_tree_add_uint_format_value(xip_tree
, hf_xip_last_node
, tvb
,
549 XIPH_LSTN
, 1, last_node
, "%d%s", last_node
,
550 last_node
== XIA_ENTRY_NODE_INDEX
? " (entry node)" : "");
552 /* Construct Destination DAG subtree. */
553 if (num_dst_nodes
> 0)
554 construct_dag(tvb
, pinfo
, xip_tree
, ett_xip_ddag
,
555 hf_xip_dst_dag
, hf_xip_dst_dag_entry
,
556 num_dst_nodes
, XIPH_DSTD
);
558 /* Construct Source DAG subtree. */
559 if (num_src_nodes
> 0)
560 construct_dag(tvb
, pinfo
, xip_tree
, ett_xip_sdag
,
561 hf_xip_src_dag
, hf_xip_src_dag_entry
,
563 XIPH_DSTD
+ num_dst_nodes
* XIA_NODE_SIZE
);
565 /* First byte after XIP header. */
566 offset
= XIPH_DSTD
+ XIA_NODE_SIZE
* (num_dst_nodes
+ num_src_nodes
);
568 /* Dissect other headers according to the sink node, if needed. */
569 offset
+= dissect_xip_sink_node(tvb
, pinfo
, tree
, offset
,
570 tvb_get_ntohl(tvb
, XIPH_DSTD
+
571 (num_dst_nodes
- 1) * XIA_NODE_SIZE
));
573 dissect_xip_next_header(tvb
, pinfo
, tree
, next_ti
, offset
);
577 dissect_xip(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
580 /* Not large enough to be valid XIP packet. */
581 if (tvb_reported_length(tvb
) < XIPH_MIN_LEN
)
584 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "XIP");
585 col_set_str(pinfo
->cinfo
, COL_INFO
, "XIP Packet");
587 display_xip(tvb
, pinfo
, tree
);
588 return tvb_captured_length(tvb
);
592 proto_register_xip(void)
594 static hf_register_info hf
[] = {
599 { "Version", "xip.version", FT_UINT8
,
600 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
603 { "Next Header", "xip.next_hdr", FT_UINT8
,
604 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
606 { &hf_xip_payload_len
,
607 { "Payload Length", "xip.payload_len", FT_UINT16
,
608 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
611 { "Hop Limit", "xip.hop_limit", FT_UINT8
,
612 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
615 { "Number of Destination Nodes", "xip.num_dst", FT_UINT8
,
616 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
619 { "Number of Source Nodes", "xip.num_src", FT_UINT8
,
620 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
623 { "Last Node", "xip.last_node", FT_UINT8
,
624 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
627 { "Destination DAG", "xip.dst_dag", FT_NONE
,
628 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
630 { &hf_xip_dst_dag_entry
,
631 { "Destination DAG Entry", "xip.dst_dag_entry", FT_STRING
,
632 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
635 { "Source DAG", "xip.src_dag", FT_NONE
,
636 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
638 { &hf_xip_src_dag_entry
,
639 { "Source DAG Entry", "xip.src_dag_entry", FT_STRING
,
640 BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}}
643 static int *ett
[] = {
649 static ei_register_info ei
[] = {
650 { &ei_xip_invalid_len
,
651 { "xip.invalid.len", PI_MALFORMED
, PI_ERROR
,
652 "Invalid length", EXPFILL
}},
654 { &ei_xip_next_header
,
655 { "xip.next.header", PI_MALFORMED
, PI_ERROR
,
656 "Invalid next header", EXPFILL
}},
658 { &ei_xip_bad_num_dst
,
659 { "xip.bad_num_dst", PI_MALFORMED
, PI_ERROR
,
660 "Invalid number of destination DAG nodes", EXPFILL
}},
662 { &ei_xip_bad_num_src
,
663 { "xip.bad_num_src", PI_MALFORMED
, PI_ERROR
,
664 "Invalid number of source DAG nodes", EXPFILL
}}
667 expert_module_t
* expert_xip
;
669 proto_xip
= proto_register_protocol("eXpressive Internet Protocol", "XIP", "xip");
671 xip_handle
= register_dissector("xip", dissect_xip
, proto_xip
);
672 proto_register_field_array(proto_xip
, hf
, array_length(hf
));
673 proto_register_subtree_array(ett
, array_length(ett
));
675 expert_xip
= expert_register_protocol(proto_xip
);
676 expert_register_field_array(expert_xip
, ei
, array_length(ei
));
680 proto_reg_handoff_xip(void)
682 dissector_add_uint("ethertype", ETHERTYPE_XIP
, xip_handle
);
684 xip_serval_handle
= find_dissector_add_dependency("xipserval", proto_xip
);
688 * Editor modelines - https://www.wireshark.org/tools/modelines.html
693 * indent-tabs-mode: t
696 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
697 * :indentSize=8:tabSize=8:noTabs=false: