/* * Copyright (c) 2004 Rinet Corp., Novosibirsk, Russia * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef linux #include #else #include #endif #include #include #include #include #include #include #include "parse_dl.h" #include "parse_ip.h" #include "netstat.h" #include "ethertype.h" #include "sll.h" #ifndef DLT_LINUX_SLL #define DLT_LINUX_SLL 113 #endif struct ether_dot1q_header { u_char dhost[ETHER_ADDR_LEN]; u_char shost[ETHER_ADDR_LEN]; u_int16_t encap_proto; u_int16_t tag; u_int16_t proto; }; int is_parse_dl(type) int type; { return (type == DLT_NULL || type == DLT_LOOP || type == DLT_EN10MB || type == DLT_SLIP || type == DLT_PPP_ETHER || type == DLT_PPP || type == DLT_RAW || type == DLT_C_HDLC || type == DLT_PPP_SERIAL || type == DLT_LINUX_SLL); } const char * parse_dl_name(type) int type; { switch (type) { case DLT_NULL: case DLT_LOOP: return "Loopback"; case DLT_EN10MB: return "Ethernet"; case DLT_SLIP: return "SLIP"; case DLT_PPP_ETHER: return "PPP over Ethernet"; case DLT_PPP: return "Async PPP"; case DLT_RAW: return "raw IP"; case DLT_C_HDLC: return "Cisco HDLC"; case DLT_PPP_SERIAL: return "Sync PPP"; case DLT_LINUX_SLL: return "Linux cooked socket"; } return "Unknown"; } int parse_dl(ns, dlt, caplen, pktlen, pkt) NETSTAT *ns; int dlt, caplen, pktlen; const u_char *pkt; { const struct ether_header *ether = 0; const struct ip *ip = 0; const u_char *p = pkt; u_int length = pktlen; u_int type, hdrlen; u_char dsap, ssap; /* sanity check */ if (!pkt) return -1; switch (dlt) { case DLT_NULL: case DLT_LOOP: if (caplen < 4) return -1; memcpy((u_char *)&type, p, sizeof(type)); if (type & 0xffff0000) { /* swap bytes */ type = (((type & 0xff) << 24) | ((type & 0xff00) << 8) | ((type & 0xff0000) >> 8) | ((type >> 24) & 0xff)); } if (type != AF_INET && type != AF_INET6) return -1; p += 4; ip = (const struct ip *)p; caplen -= 4; length -= 4; break; case DLT_EN10MB: hdrlen = sizeof(struct ether_header); if (caplen < hdrlen) return -1; ether = (struct ether_header *)p; if (ns) { #ifdef HAVE_ETHER_ADDR memcpy(ns->eth_src_addr, ðer->ether_shost, sizeof(struct ether_addr)); memcpy(ns->eth_dst_addr, ðer->ether_dhost, sizeof(struct ether_addr)); #else memcpy(ns->eth_src_addr, ether->ether_shost, ETHER_ADDR_LEN); memcpy(ns->eth_dst_addr, ether->ether_dhost, ETHER_ADDR_LEN); #endif ns->eth_type = ether->ether_type; } type = ntohs(ether->ether_type); if (type <= ETHERMTU) { /* IEEE 802.3 frame: the type is data length */ if (caplen < hdrlen + 3) return -1; /* extract SAP (Service Access Point) IDs */ dsap = p[hdrlen]; ssap = p[hdrlen + 1]; if (ns) { ns->eth_dsap = dsap; ns->eth_ssap = ssap; ns->data_len = type; } type = 0; /* no type known yet */ hdrlen += 3; /* skip 802.2 LLC header */ if (dsap == 0x06 && ssap == 0x06) { /* 802.3/802.2 encapsulated IP */ type = ETHERTYPE_IP; } else if (dsap == 0xAA && ssap == 0xAA) { if (caplen < hdrlen + 5) return -1; /* extract encap type after 3-bytes OUI */ type = *(u_int16_t *)(p + hdrlen + 3); if (ns) ns->eth_type = type; type = ntohs(type); hdrlen += 5; /* skip 802.2 SNAP header */ } } else if (type == ETHERTYPE_8021Q) { hdrlen = sizeof(struct ether_dot1q_header); if (caplen < hdrlen) return -1; if (ns) ns->eth_tag = ((struct ether_dot1q_header *)p)->tag; type = ntohs(((struct ether_dot1q_header *)p)->proto); } p += hdrlen; if (type == ETHERTYPE_IP || type == ETHERTYPE_IPV6) ip = (const struct ip *)p; caplen -= hdrlen; length -= hdrlen; break; case DLT_SLIP: if (caplen < 16) return -1; p += 16; ip = (const struct ip *)p; caplen -= 16; length -= 16; break; case DLT_PPP_ETHER: if (caplen < 6 || p[1]) return -1; p += 6; caplen -= 6; length -= 6; /* pass through */ case DLT_PPP: if (caplen < 4) return -1; hdrlen = 0; #ifdef SLC_BPFHDR if (dlt == DLT_PPP) { ip = (const struct ip *)(p + SLC_BPFHDR);/* skip bpf pseudo header */ p += SLC_BPFHDRLEN; /* now pointer to link level header */ } #endif /* PPP address and PPP control fields may be present (-acfc) */ if (p[0] == 0xff && p[1] == 0x03) { p += 2; hdrlen += 2; } /* retrive the protocol type */ if (*p & 01) { /* compressed protocol field (pfc) */ type = *p++; hdrlen++; } else { /* un-compressed protocol field (-pfc) */ type = ntohs(*(u_int16_t *)p); p += 2; hdrlen += 2; } /* check for IP or IPv6 */ if (type != 0x21 && type != 0x57 && type != ETHERTYPE_IP && type != ETHERTYPE_IPV6) return -1; #ifdef SLC_BPFHDR if (dlt == DLT_PPP) { caplen -= SLC_BPFHDR; length -= SLC_BPFHDR; } else { ip = (const struct ip *)p; caplen -= hdrlen; length -= hdrlen; } #else ip = (const struct ip *)p; caplen -= hdrlen; length -= hdrlen; #endif break; case DLT_RAW: ip = (const struct ip *)p; break; case DLT_C_HDLC: case DLT_PPP_SERIAL: if (caplen < 4) return -1; /* check for UNICAST or BCAST */ if (p[0] != 0x0f && p[0] != 0x8f) return -1; type = ntohs(*(u_int16_t *)&p[2]); if (type != ETHERTYPE_IP && type != ETHERTYPE_IPV6) return -1; p += 4; ip = (const struct ip *)p; caplen -= 4; length -= 4; break; case DLT_LINUX_SLL: if (caplen < SLL_HDR_LEN) return -1; if (ntohs(((struct sll_header *)p)->sll_halen) == ETHER_ADDR_LEN) { if (ns) { /* the source address is in the packet header */ memcpy(ns->eth_src_addr, ((struct sll_header *)p)->sll_addr, ETHER_ADDR_LEN); /* just a fake the destination address */ memset(ns->eth_dst_addr, 0, ETHER_ADDR_LEN); type = ntohs(((struct sll_header *)p)->sll_pkttype); if (type != LINUX_SLL_OUTGOING) { if (type == LINUX_SLL_BROADCAST) memset(ns->eth_dst_addr, 0xff, ETHER_ADDR_LEN); else if (type == LINUX_SLL_MULTICAST) ns->eth_dst_addr[0] = 1; else ns->eth_dst_addr[ETHER_ADDR_LEN-1] = 1; } ns->eth_type = ((struct sll_header *)p)->sll_protocol; } /* point somewhere to avoid return after switch() */ ether = (struct ether_header *)p; } type = ntohs(((struct sll_header *)p)->sll_protocol); p += SLL_HDR_LEN; caplen -= SLL_HDR_LEN; length -= SLL_HDR_LEN; if (type == ETHERTYPE_IP || type == ETHERTYPE_IPV6) ip = (const struct ip *)p; break; default: /* Unknown or unsupported data link type */ return -1; } if (!ether && !ip) return -1; if (caplen > length) caplen = length; if (ns) { if (!ns->pkt_cnt) ns->pkt_cnt = 1; if (!ns->pkt_len) ns->pkt_len = pktlen; if (!ns->data_len) ns->data_len = length; } hdrlen = (char *)p - (char *)pkt; if (ip && caplen >= sizeof(struct ip)) { int hlen = parse_ip(ns, caplen, ip); if (hlen > 0) hdrlen += hlen; } return hdrlen; }