/* eth_parser.c * * This file contains functions to parse Ethernet II headers. * * File begun on 2018-11-13 * * Created by: * - Théo Bertin (theo.bertin@advens.fr) * * With: * - François Bernard (francois.bernard@isen.yncrea.fr) * - Tianyu Geng (tianyu.geng@isen.yncrea.fr) * * This file is part of rsyslog. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * -or- * see COPYING.ASL20 in the source distribution * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "parsers.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpacked" #pragma GCC diagnostic ignored "-Wattributes" struct __attribute__ ((__packed__)) eth_header_s { uint8_t addrDst[6]; uint8_t addrSrc[6]; uint16_t type; }; struct __attribute__ ((__packed__)) vlan_header_s { uint8_t addrDst[6]; uint8_t addrSrc[6]; uint16_t vlanCode; uint16_t vlanTag; uint16_t type; }; #pragma GCC diagnostic pop typedef struct eth_header_s eth_header_t; typedef struct vlan_header_s vlan_header_t; /* * Get an ethernet header type as uint16_t * and return the correspondence as string * NOTE : Only most common types are present, to complete if needed */ static const char *eth_type_to_string(uint16_t eth_type) { switch (eth_type) { case 0x00bb: // Extreme Networks Discovery Protocol return "EDP"; case 0x0200: // PUP protocol return "PUP"; case 0x0800: // IP protocol return "IP"; case 0x0806: // address resolution protocol return "ARP"; case 0x88a2: // AoE protocol return "AOE"; case 0x2000: // Cisco Discovery Protocol return "CDP"; case 0x2004: // Cisco Dynamic Trunking Protocol return "DTP"; case 0x8035: // reverse addr resolution protocol return "REVARP"; case 0x8100: // IEEE 802.1Q VLAN tagging return "802.1Q"; case 0x88a8: // IEEE 802.1ad return "802.1AD"; case 0x9100: // Legacy QinQ return "QINQ1"; case 0x9200: // Legacy QinQ return "QINQ2"; case 0x8137: // Internetwork Packet Exchange return "IPX"; case 0x86DD: // IPv6 protocol return "IPv6"; case 0x880B: // PPP return "PPP"; case 0x8847: // MPLS return "MPLS"; case 0x8848: // MPLS Multicast return "MPLS_MCAST"; case 0x8863: // PPP Over Ethernet Discovery Stage return "PPPoE_DISC"; case 0x8864: // PPP Over Ethernet Session Stage return "PPPoE"; case 0x88CC: // Link Layer Discovery Protocol return "LLDP"; case 0x6558: // Transparent Ethernet Bridging return "TEB"; default: return "UNKNOWN"; } } /* * This function parses the bytes in the received packet to extract Ethernet II metadata. * * its parameters are: * - a pointer on the list of bytes representing the packet * the first byte must be the beginning of the ETH header * - the size of the list passed as first parameter * - a pointer on a json_object, containing all the metadata recovered so far * this is also where ETH metadata will be added * * This function returns a structure containing the data unprocessed by this parser * or the ones after (as a list of bytes), and the length of this data. */ data_ret_t *eth_parse(const uchar *packet, int pktSize, struct json_object *jparent) { DBGPRINTF("entered eth_parse\n"); DBGPRINTF("packet size %d\n", pktSize); if (pktSize < 14) { /* too short for eth header */ DBGPRINTF("ETH packet too small : %d\n", pktSize); RETURN_DATA_AFTER(0) } eth_header_t *eth_header = (eth_header_t *)packet; char ethMacSrc[20], ethMacDst[20]; uint8_t hdrLen = 14; ether_ntoa_r((struct ether_addr *)eth_header->addrSrc, ethMacSrc); ether_ntoa_r((struct ether_addr *)eth_header->addrDst, ethMacDst); json_object_object_add(jparent, "ETH_src", json_object_new_string((char *)ethMacSrc)); json_object_object_add(jparent, "ETH_dst", json_object_new_string((char *)ethMacDst)); uint16_t ethType = (uint16_t)ntohs(eth_header->type); if (ethType == ETHERTYPE_VLAN) { vlan_header_t *vlan_header = (vlan_header_t *)packet; json_object_object_add(jparent, "ETH_tag", json_object_new_int(ntohs(vlan_header->vlanTag))); ethType = (uint16_t)ntohs(vlan_header->type); hdrLen += 4; } data_ret_t *ret; if (ethType < 1500) { /* this is a LLC header */ json_object_object_add(jparent, "ETH_len", json_object_new_int(ethType)); ret = llc_parse(packet + hdrLen, pktSize - hdrLen, jparent); /* packet has the minimum allowed size, so the remaining data is * most likely padding, this should not appear as data, so remove it * */ //TODO this is a quick win, a more elaborate solution would be to check if all data // is indeed zero, but that would take more processing time if (pktSize <= 60 && ret->pData != NULL) { if (!ret->pData[0]) ret->size = 0; } return ret; } json_object_object_add(jparent, "ETH_type", json_object_new_int(ethType)); json_object_object_add(jparent, "ETH_typestr", json_object_new_string((char *)eth_type_to_string(ethType))); ret = eth_proto_parse(ethType, (packet + hdrLen), (pktSize - hdrLen), jparent); /* packet has the minimum allowed size, so the remaining data is * most likely padding, this should not appear as data, so remove it */ if (pktSize <= 60 && ret->pData != NULL) { if (!ret->pData[0]) ret->size = 0; } return ret; }