1 /*
2  * Copyright (c) 2009
3  * 	Siemens AG, All rights reserved.
4  * 	Dmitry Eremin-Solenikov (dbaryshkov@gmail.com)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that: (1) source code distributions
8  * retain the above copyright notice and this paragraph in its entirety, (2)
9  * distributions including binary code include the above copyright notice and
10  * this paragraph in its entirety in the documentation or other materials
11  * provided with the distribution, and (3) all advertising materials mentioning
12  * features or use of this software display the following acknowledgement:
13  * ``This product includes software developed by the University of California,
14  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
15  * the University nor the names of its contributors may be used to endorse
16  * or promote products derived from this software without specific prior
17  * written permission.
18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
19  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
20  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21  */
22 
23 /* \summary: IEEE 802.15.4 printer */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <netdissect-stdinc.h>
30 
31 #include "netdissect.h"
32 #include "addrtoname.h"
33 
34 #include "extract.h"
35 
36 static const char *ftypes[] = {
37 	"Beacon",			/* 0 */
38 	"Data",				/* 1 */
39 	"ACK",				/* 2 */
40 	"Command",			/* 3 */
41 	"Reserved (0x4)",		/* 4 */
42 	"Reserved (0x5)",		/* 5 */
43 	"Reserved (0x6)",		/* 6 */
44 	"Reserved (0x7)",		/* 7 */
45 };
46 
47 /*
48  * Frame Control subfields.
49  */
50 #define FC_FRAME_TYPE(fc)		((fc) & 0x7)
51 #define FC_SECURITY_ENABLED		0x0008
52 #define FC_FRAME_PENDING		0x0010
53 #define FC_ACK_REQUEST			0x0020
54 #define FC_PAN_ID_COMPRESSION		0x0040
55 #define FC_DEST_ADDRESSING_MODE(fc)	(((fc) >> 10) & 0x3)
56 #define FC_FRAME_VERSION(fc)		(((fc) >> 12) & 0x3)
57 #define FC_SRC_ADDRESSING_MODE(fc)	(((fc) >> 14) & 0x3)
58 
59 #define FC_ADDRESSING_MODE_NONE		0x00
60 #define FC_ADDRESSING_MODE_RESERVED	0x01
61 #define FC_ADDRESSING_MODE_SHORT	0x02
62 #define FC_ADDRESSING_MODE_LONG		0x03
63 
64 u_int
65 ieee802_15_4_if_print(netdissect_options *ndo,
66                       const struct pcap_pkthdr *h, const u_char *p)
67 {
68 	u_int caplen = h->caplen;
69 	u_int hdrlen;
70 	uint16_t fc;
71 	uint8_t seq;
72 	uint16_t panid = 0;
73 
74 	if (caplen < 3) {
75 		ND_PRINT((ndo, "[|802.15.4]"));
76 		return caplen;
77 	}
78 	hdrlen = 3;
79 
80 	fc = EXTRACT_LE_16BITS(p);
81 	seq = EXTRACT_LE_8BITS(p + 2);
82 
83 	p += 3;
84 	caplen -= 3;
85 
86 	ND_PRINT((ndo,"IEEE 802.15.4 %s packet ", ftypes[FC_FRAME_TYPE(fc)]));
87 	if (ndo->ndo_vflag)
88 		ND_PRINT((ndo,"seq %02x ", seq));
89 
90 	/*
91 	 * Destination address and PAN ID, if present.
92 	 */
93 	switch (FC_DEST_ADDRESSING_MODE(fc)) {
94 	case FC_ADDRESSING_MODE_NONE:
95 		if (fc & FC_PAN_ID_COMPRESSION) {
96 			/*
97 			 * PAN ID compression; this requires that both
98 			 * the source and destination addresses be present,
99 			 * but the destination address is missing.
100 			 */
101 			ND_PRINT((ndo, "[|802.15.4]"));
102 			return hdrlen;
103 		}
104 		if (ndo->ndo_vflag)
105 			ND_PRINT((ndo,"none "));
106 		break;
107 	case FC_ADDRESSING_MODE_RESERVED:
108 		if (ndo->ndo_vflag)
109 			ND_PRINT((ndo,"reserved destination addressing mode"));
110 		return hdrlen;
111 	case FC_ADDRESSING_MODE_SHORT:
112 		if (caplen < 2) {
113 			ND_PRINT((ndo, "[|802.15.4]"));
114 			return hdrlen;
115 		}
116 		panid = EXTRACT_LE_16BITS(p);
117 		p += 2;
118 		caplen -= 2;
119 		hdrlen += 2;
120 		if (caplen < 2) {
121 			ND_PRINT((ndo, "[|802.15.4]"));
122 			return hdrlen;
123 		}
124 		if (ndo->ndo_vflag)
125 			ND_PRINT((ndo,"%04x:%04x ", panid, EXTRACT_LE_16BITS(p)));
126 		p += 2;
127 		caplen -= 2;
128 		hdrlen += 2;
129 		break;
130 	case FC_ADDRESSING_MODE_LONG:
131 		if (caplen < 2) {
132 			ND_PRINT((ndo, "[|802.15.4]"));
133 			return hdrlen;
134 		}
135 		panid = EXTRACT_LE_16BITS(p);
136 		p += 2;
137 		caplen -= 2;
138 		hdrlen += 2;
139 		if (caplen < 8) {
140 			ND_PRINT((ndo, "[|802.15.4]"));
141 			return hdrlen;
142 		}
143 		if (ndo->ndo_vflag)
144 			ND_PRINT((ndo,"%04x:%s ", panid, le64addr_string(ndo, p)));
145 		p += 8;
146 		caplen -= 8;
147 		hdrlen += 8;
148 		break;
149 	}
150 	if (ndo->ndo_vflag)
151 		ND_PRINT((ndo,"< "));
152 
153 	/*
154 	 * Source address and PAN ID, if present.
155 	 */
156 	switch (FC_SRC_ADDRESSING_MODE(fc)) {
157 	case FC_ADDRESSING_MODE_NONE:
158 		if (ndo->ndo_vflag)
159 			ND_PRINT((ndo,"none "));
160 		break;
161 	case FC_ADDRESSING_MODE_RESERVED:
162 		if (ndo->ndo_vflag)
163 			ND_PRINT((ndo,"reserved source addressing mode"));
164 		return 0;
165 	case FC_ADDRESSING_MODE_SHORT:
166 		if (!(fc & FC_PAN_ID_COMPRESSION)) {
167 			/*
168 			 * The source PAN ID is not compressed out, so
169 			 * fetch it.  (Otherwise, we'll use the destination
170 			 * PAN ID, fetched above.)
171 			 */
172 			if (caplen < 2) {
173 				ND_PRINT((ndo, "[|802.15.4]"));
174 				return hdrlen;
175 			}
176 			panid = EXTRACT_LE_16BITS(p);
177 			p += 2;
178 			caplen -= 2;
179 			hdrlen += 2;
180 		}
181 		if (caplen < 2) {
182 			ND_PRINT((ndo, "[|802.15.4]"));
183 			return hdrlen;
184 		}
185 		if (ndo->ndo_vflag)
186 			ND_PRINT((ndo,"%04x:%04x ", panid, EXTRACT_LE_16BITS(p)));
187 		p += 2;
188 		caplen -= 2;
189 		hdrlen += 2;
190 		break;
191 	case FC_ADDRESSING_MODE_LONG:
192 		if (!(fc & FC_PAN_ID_COMPRESSION)) {
193 			/*
194 			 * The source PAN ID is not compressed out, so
195 			 * fetch it.  (Otherwise, we'll use the destination
196 			 * PAN ID, fetched above.)
197 			 */
198 			if (caplen < 2) {
199 				ND_PRINT((ndo, "[|802.15.4]"));
200 				return hdrlen;
201 			}
202 			panid = EXTRACT_LE_16BITS(p);
203 			p += 2;
204 			caplen -= 2;
205 			hdrlen += 2;
206 		}
207 		if (caplen < 8) {
208 			ND_PRINT((ndo, "[|802.15.4]"));
209 			return hdrlen;
210 		}
211 		if (ndo->ndo_vflag)
212 			ND_PRINT((ndo,"%04x:%s ", panid, le64addr_string(ndo, p)));
213 		p += 8;
214 		caplen -= 8;
215 		hdrlen += 8;
216 		break;
217 	}
218 
219 	if (!ndo->ndo_suppress_default_print)
220 		ND_DEFAULTPRINT(p, caplen);
221 
222 	return hdrlen;
223 }
224