1 /* eth_parser.c
2  *
3  * This file contains functions to parse Ethernet II headers.
4  *
5  * File begun on 2018-11-13
6  *
7  * Created by:
8  *  - Théo Bertin (theo.bertin@advens.fr)
9  *
10  * With:
11  *  - François Bernard (francois.bernard@isen.yncrea.fr)
12  *  - Tianyu Geng (tianyu.geng@isen.yncrea.fr)
13  *
14  * This file is part of rsyslog.
15  *
16  * Licensed under the Apache License, Version 2.0 (the "License");
17  * you may not use this file except in compliance with the License.
18  * You may obtain a copy of the License at
19  *
20  *       http://www.apache.org/licenses/LICENSE-2.0
21  *       -or-
22  *       see COPYING.ASL20 in the source distribution
23  *
24  * Unless required by applicable law or agreed to in writing, software
25  * distributed under the License is distributed on an "AS IS" BASIS,
26  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27  * See the License for the specific language governing permissions and
28  * limitations under the License.
29  */
30 
31 #include "config.h"
32 #include "parsers.h"
33 
34 #pragma GCC diagnostic push
35 #pragma GCC diagnostic ignored "-Wpacked"
36 #pragma GCC diagnostic ignored "-Wattributes"
37 struct __attribute__ ((__packed__)) eth_header_s {
38 	uint8_t addrDst[6];
39 	uint8_t addrSrc[6];
40 	uint16_t type;
41 };
42 
43 struct __attribute__ ((__packed__)) vlan_header_s {
44 	uint8_t addrDst[6];
45 	uint8_t addrSrc[6];
46 	uint16_t vlanCode;
47 	uint16_t vlanTag;
48 	uint16_t type;
49 };
50 #pragma GCC diagnostic pop
51 
52 typedef struct eth_header_s eth_header_t;
53 typedef struct vlan_header_s vlan_header_t;
54 
55 
56 /*
57  *  Get an ethernet header type as uint16_t
58  *   and return the correspondence as string
59  *  NOTE : Only most common types are present, to complete if needed
60  */
eth_type_to_string(uint16_t eth_type)61 static const char *eth_type_to_string(uint16_t eth_type) {
62 	switch (eth_type) {
63 		case 0x00bb:        // Extreme Networks Discovery Protocol
64 			return "EDP";
65 		case 0x0200:        // PUP protocol
66 			return "PUP";
67 		case 0x0800:        // IP protocol
68 			return "IP";
69 		case 0x0806:        // address resolution protocol
70 			return "ARP";
71 		case 0x88a2:        // AoE protocol
72 			return "AOE";
73 		case 0x2000:        // Cisco Discovery Protocol
74 			return "CDP";
75 		case 0x2004:        // Cisco Dynamic Trunking Protocol
76 			return "DTP";
77 		case 0x8035:        // reverse addr resolution protocol
78 			return "REVARP";
79 		case 0x8100:        // IEEE 802.1Q VLAN tagging
80 			return "802.1Q";
81 		case 0x88a8:        // IEEE 802.1ad
82 			return "802.1AD";
83 		case 0x9100:        // Legacy QinQ
84 			return "QINQ1";
85 		case 0x9200:        // Legacy QinQ
86 			return "QINQ2";
87 		case 0x8137:        // Internetwork Packet Exchange
88 			return "IPX";
89 		case 0x86DD:        // IPv6 protocol
90 			return "IPv6";
91 		case 0x880B:        // PPP
92 			return "PPP";
93 		case 0x8847:        // MPLS
94 			return "MPLS";
95 		case 0x8848:        // MPLS Multicast
96 			return "MPLS_MCAST";
97 		case 0x8863:        // PPP Over Ethernet Discovery Stage
98 			return "PPPoE_DISC";
99 		case 0x8864:        // PPP Over Ethernet Session Stage
100 			return "PPPoE";
101 		case 0x88CC:        // Link Layer Discovery Protocol
102 			return "LLDP";
103 		case 0x6558:        // Transparent Ethernet Bridging
104 			return "TEB";
105 		default:
106 			return "UNKNOWN";
107 	}
108 }
109 
110 
111 /*
112  *  This function parses the bytes in the received packet to extract Ethernet II metadata.
113  *
114  *  its parameters are:
115  *    - a pointer on the list of bytes representing the packet
116  *        the first byte must be the beginning of the ETH header
117  *    - the size of the list passed as first parameter
118  *    - a pointer on a json_object, containing all the metadata recovered so far
119  *      this is also where ETH metadata will be added
120  *
121  *  This function returns a structure containing the data unprocessed by this parser
122  *  or the ones after (as a list of bytes), and the length of this data.
123 */
eth_parse(const uchar * packet,int pktSize,struct json_object * jparent)124 data_ret_t *eth_parse(const uchar *packet, int pktSize, struct json_object *jparent) {
125 	DBGPRINTF("entered eth_parse\n");
126 	DBGPRINTF("packet size %d\n", pktSize);
127 	if (pktSize < 14) {  /* too short for eth header */
128 		DBGPRINTF("ETH packet too small : %d\n", pktSize);
129 		RETURN_DATA_AFTER(0)
130 	}
131 
132 	eth_header_t *eth_header = (eth_header_t *)packet;
133 	char ethMacSrc[20], ethMacDst[20];
134 	uint8_t hdrLen = 14;
135 
136 	ether_ntoa_r((struct ether_addr *)eth_header->addrSrc, ethMacSrc);
137 	ether_ntoa_r((struct ether_addr *)eth_header->addrDst, ethMacDst);
138 
139 	json_object_object_add(jparent, "ETH_src", json_object_new_string((char *)ethMacSrc));
140 	json_object_object_add(jparent, "ETH_dst", json_object_new_string((char *)ethMacDst));
141 
142 	uint16_t ethType = (uint16_t)ntohs(eth_header->type);
143 
144 	if (ethType == ETHERTYPE_VLAN) {
145 		vlan_header_t *vlan_header = (vlan_header_t *)packet;
146 		json_object_object_add(jparent, "ETH_tag", json_object_new_int(ntohs(vlan_header->vlanTag)));
147 		ethType = (uint16_t)ntohs(vlan_header->type);
148 		hdrLen += 4;
149 	}
150 
151 	data_ret_t *ret;
152 
153 	if (ethType < 1500) {
154 		/* this is a LLC header */
155 		json_object_object_add(jparent, "ETH_len", json_object_new_int(ethType));
156 		ret = llc_parse(packet + hdrLen, pktSize - hdrLen, jparent);
157 
158 		/* packet has the minimum allowed size, so the remaining data is
159 		 * most likely padding, this should not appear as data, so remove it
160 		 * */
161 		//TODO this is a quick win, a more elaborate solution would be to check if all data
162 		// is indeed zero, but that would take more processing time
163 		if (pktSize <= 60 && ret->pData != NULL) {
164 			if (!ret->pData[0]) ret->size = 0;
165 		}
166 		return ret;
167 	}
168 
169 	json_object_object_add(jparent, "ETH_type", json_object_new_int(ethType));
170 	json_object_object_add(jparent, "ETH_typestr", json_object_new_string((char *)eth_type_to_string(ethType)));
171 	ret = eth_proto_parse(ethType, (packet + hdrLen), (pktSize - hdrLen), jparent);
172 
173 	/* packet has the minimum allowed size, so the remaining data is
174 	 * most likely padding, this should not appear as data, so remove it */
175 	if (pktSize <= 60 && ret->pData != NULL) {
176 		if (!ret->pData[0]) ret->size = 0;
177 	}
178 	return ret;
179 }
180