#!/usr/sbin/dtrace -Cs /* * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License") * (see http://www.opensource.org/licenses/CDDL-1.0). * You may not use this file except in compliance with the License. * * Copyright (c) 2011 Jens Elkner. All rights reserved. * Use is subject to license terms. */ /** * Traces incoming and outgoing ARP packets (Ethernet only) like * 'tcpdump -ennqti $ifname arp' or 'snoop -d $ifname arp'. However, it tracks * all interfaces at once (i.e. no need to start an instance for every single * interface) and provides a better human readable output, than similar tools. * * NOTE: * On Solaris Express, header files are per default not installed. Fix with: * pkg install system/header */ #pragma D option quiet #include /* use '$0 -DDROP' to see most of the ARP drop msgs (i.e. which are ignored by the kernel) as well. '-DDEBUG' enables output of more details wrt. the ARP message. */ #define ETHERTYPE_VLAN 0x8100 /* 802.1Q VLAN */ #define ARH_FIXED_LEN 8 /* ARP header length in octets */ #define ETHERADDRL 6 /* ethernet address length in octest */ #define IPV4_ADDR_LEN 4 /* IP addr length in octets */ #define VLAN_ETH_SZ sizeof(struct ether_vlan_header) #define ETH_SZ sizeof(struct ether_header) #define VLAN_ID_MASK 0x0fffu #define VLAN_ID(tci) ((tci) & VLAN_ID_MASK) #define _PTRDIFF(p1, p2) ((intptr_t)((uintptr_t)(p1) - (uintptr_t)(p2))) #define MBLKL(mp) _PTRDIFF((mp)->b_wptr, (mp)->b_rptr) string DEC2HEX; uchar_t NULLSTR[8]; #define OP_UNKNOWN 0 #define OP_ARP_R 1 #define OP_ARP_A 2 #define OP_RARP_R 3 #define OP_RARP_A 4 #define OP_MAX 5 #define R_RARP 0 #define R_UNARP 1 #define R_ANNOUNCE 2 #define R_GRAT 3 #define R_PROBE 4 #define R_NORM 5 #define A_NORM 6 #define A_RARP 7 #define MSG_MAX 8 string OP[OP_MAX][2]; string MSG[MSG_MAX][5]; /* Unfortunately dtrace does not support printf positional args :(( */ #define NEWMSG(a, b, c, d, e, f) \ MSG[a][0] = b; MSG[a][1] = c; MSG[a][2] = d; MSG[a][3] = e; MSG[a][4] = f; dtrace:::BEGIN { DEC2HEX = "0123456789abcdef"; NULLSTR[0] = 0; NULLSTR[1] = 0; NULLSTR[2] = 0; NULLSTR[3] = 0; NULLSTR[4] = 0; NULLSTR[5] = 0; NULLSTR[6] = 0; NULLSTR[7] = 0; OP[OP_UNKNOWN][0] = "?"; OP[OP_UNKNOWN][1] = "packet "; OP[OP_ARP_R][0] = "R"; OP[OP_ARP_R][1] = "request to resolve address "; OP[OP_ARP_A][0] = "A"; OP[OP_ARP_A][1] = "response to previous request "; OP[OP_RARP_R][0] = "RR"; OP[OP_RARP_R][1] = "Reverse ARP request "; OP[OP_RARP_A][0] = "RA"; OP[OP_RARP_A][1] = "Reverse ARP reply "; /* Unfortunately dtrace does not support printf positional args :(( */ NEWMSG(R_NORM, "Who is ", "? Tell ", "[", "]", ""); NEWMSG(R_ANNOUNCE, "", " is ", " announcement", "", ""); NEWMSG(R_GRAT, "", " is ", " gratuitous announcement", "", ""); NEWMSG(R_PROBE, "Does anybody use ", " ? Tell ", "", "", ""); NEWMSG(R_UNARP, "Remove ", " from your ARP cache!", "", "", ""); NEWMSG(R_RARP, "Who is ", " ?", "", "", ""); NEWMSG(A_RARP, "", " is ", ", answered by ", "[", "]"); NEWMSG(A_NORM, "", " is ", ", answering ", "[", "]"); } #define MAC2STR(mac, dst) \ dst = (char *) alloca(ETHERADDRL*3); \ dst[2] = dst[5] = dst[8] = dst[11] = dst[14] = ':'; dst[17] = '\0'; \ dst[0] = DEC2HEX[(mac)[0] >> 4 & 0x0f]; dst[1] = DEC2HEX[(mac)[0] & 0x0f]; \ dst[3] = DEC2HEX[(mac)[1] >> 4 & 0x0f]; dst[4] = DEC2HEX[(mac)[1] & 0x0f]; \ dst[6] = DEC2HEX[(mac)[2] >> 4 & 0x0f]; dst[7] = DEC2HEX[(mac)[2] & 0x0f]; \ dst[9] = DEC2HEX[(mac)[3] >> 4 & 0x0f]; dst[10] = DEC2HEX[(mac)[3] & 0x0f]; \ dst[12] = DEC2HEX[(mac)[4] >> 4 & 0x0f]; dst[13] = DEC2HEX[(mac)[4] & 0x0f]; \ dst[15] = DEC2HEX[(mac)[5] >> 4 & 0x0f]; dst[16] = DEC2HEX[(mac)[5] & 0x0f]; #define IP2STR(ip, dst) \ dst = lltostr((ulong_t) (ip)[0]); \ dst = strjoin(dst, "."); \ dst = strjoin(dst, lltostr((ulong_t) (ip)[1])); \ dst = strjoin(dst, "."); \ dst = strjoin(dst, lltostr((ulong_t) (ip)[2])); \ dst = strjoin(dst, "."); \ dst = strjoin(dst, lltostr((ulong_t) (ip)[3])); \ /* see ip_arp.c::arp_process_packet(...) 898 */ /* see ip_arp.c::arp_output(...) 1548 */ /** * @param arg0 ill_t *ill. Always != NULL. * @param arg1 arh_t *arh. Always != NULL. When called, it is safe to assume * that (arh->hlen == 0 OR arh->hlen == ill->ill_phys_addr_length) * AND arh->plen > 0. * @param arg2 mblk_t *mp. Always != NULL. Data = ARP header + payload. * Safe: length(data) >= ARH_FIXED_LEN + 2*(hlen+plen) */ sdt:ip:arp_process_packet:arp-physical-in-start, sdt:ip:arp_output:arp-physical-out-start / ((ill_t *) arg0)->ill_phys_addr_length == ETHERADDRL && (uint32_t)((arh_t *) arg1)->arh_operation[1] < 5 && (uint32_t)((arh_t *) arg1)->arh_plen == IPV4_ADDR_LEN / { this->hlen = (uint32_t)((arh_t *) arg1)->arh_hlen; this->op = (uint32_t)((arh_t *) arg1)->arh_operation[1]; #ifdef DEBUG this->p = (uchar_t *)(arg1 - sizeof(ushort_t)); this->eth_sz = *(ushort_t *) this->p == htons(ETHERTYPE_VLAN) ? VLAN_ETH_SZ : ETH_SZ; this->p = (uchar_t *)(arg1 - this->eth_sz); this->vlan = this->eth_sz != VLAN_ETH_SZ ? -1 : VLAN_ID(ntohs(((struct ether_vlan_header *) this->p)->ether_tci)); MAC2STR(this->p, this->eth_dea); this->p += ETHERADDRL; MAC2STR(this->p, this->eth_sea); #endif this->p = (uchar_t *)(arg1 + ARH_FIXED_LEN); this->tmp = this->hlen ? this->p : (uchar_t *) NULLSTR; MAC2STR(this->tmp, this->sea); this->p += this->hlen; IP2STR(this->p, this->sip); this->p += IPV4_ADDR_LEN; this->tmp = this->hlen ? this->p : NULLSTR; MAC2STR(this->tmp, this->dea); this->p += this->hlen; IP2STR(this->p, this->dip); this->action = this->hlen == 0 ? R_UNARP : (string) this->sip == "0.0.0.0" ? R_PROBE : this->sip == this->dip ? (this->op == OP_ARP_R ? R_ANNOUNCE : R_GRAT) : (string) this->sea == (string) this->dea && this->op == OP_RARP_R ? R_RARP : this->op == OP_RARP_A ? A_RARP : this->op == OP_ARP_R ? R_NORM : A_NORM; /* Unfortunately dtrace does not support positional args :(( R_RARP dea, ---, ---, --- R_UNARP sip, ---, ---, --- R_ANNOUNCE dip, sea, ---, --- R_GRAT dip, sea, ---, --- R_PROBE dip, sea, ---, --- R_NORM dip, sip, sea, --- A_NORM sip, sea, dip, dea A_RARP dea, dip, sip, sea */ printf( #ifndef DEBUG "%Y %3s [%s]: %s%s%s%s%s%s%s%s%s\n" #else "%Y %3s [%s]: %s%s%s%s%s%s%s%s%s\n HLEN=%d OP=%s VLAN=%s\n ETH: %s -> %s\n ARP: %s/%s -> %s/%s\n\n" #endif , walltimestamp, probename == "arp-physical-in-start" ? "IN" : "OUT", (string) ((ill_t *) arg0)->ill_name, /* param 1 */ MSG[this->action][0], this->action == R_RARP || this->action == A_RARP ? (string) this->dea : this->action == R_UNARP || this->action == A_NORM ? this->sip : this->dip, /* param 2 */ MSG[this->action][1], this->action == R_NORM ? this->sip : this->action == A_RARP ? this->dip : this->action > R_UNARP ? this->sea : "", /* param 3 */ MSG[this->action][2], this->action == A_RARP ? this->sip : this->action == A_NORM ? this->dip : this->action == R_NORM ? this->sea : "", /* param 4 */ MSG[this->action][3], this->action == A_RARP ? this->sea : this->action == A_NORM ? this->dea : "", MSG[this->action][4] #ifdef DEBUG ,this->hlen, OP[this->op][0], this->vlan == -1 ? "default" : lltostr((ulong_t) this->vlan), (string) this->eth_sea, (string) this->eth_dea, (string) this->sea, this->sip, (string) this->dea, this->dip #endif ); } #ifdef DROP #define WRAPPER_SZ sizeof(dl_unitdata_ind_wrapper_t) /* @param args[0] error_msg. always != NULL, const char * * @param args[1] mp. either arph_t or dl_unit_ind_wrapper_t mblk. If the * latter, mp->b_cont contains the arph_t mblk except if * arh_t->plen != 4, than mp->b_cont == NULL. The arph_t data * usually contain padding bytes to reach min packet size. * @param args[2] ill. might be NULL if no ill is avail - would happen before * arp_process_packet(ill, mp) */ fbt:ip:arp_drop_packet:entry / args[2] && args[2]->ill_phys_addr_length == ETHERADDRL / { this->ud_dp = args[1] && MBLKL(args[1]) == WRAPPER_SZ ? (dl_unitdata_ind_wrapper_t *) args[1]->b_rptr : NULL; this->arp_mp = this->ud_dp ? args[1]->b_cont : args[1]; this->arp_dp = this->arp_mp ? this->arp_mp->b_rptr : NULL; this->arp_sz = this->arp_dp ? MBLKL(this->arp_mp) : 0; this->eth_sz = this->arp_dp ? (*(ushort_t *) this->arp_dp - 2) == htons(ETHERTYPE_VLAN) ? VLAN_ETH_SZ : ETH_SZ : 0; this->eth_dp = this->eth_sz ? (struct ether_header *) (this->arp_dp - this->eth_sz) : NULL; this->vlan = this->eth_sz == VLAN_ETH_SZ ? ((struct ether_vlan_header *) this->eth_dp)->ether_tci : 0; this->eth_sea_p = this->ud_dp && this->ud_dp->dl_unitdata.dl_src_addr_length ? (uchar_t*) this->ud_dp->dl_src_addr : (this->eth_dp ? (uchar_t*) &(this->eth_dp->ether_shost) : NULL); this->eth_dea_p = this->eth_dp ? (uchar_t*) &(this->eth_dp->ether_dhost) : (this->ud_dp && this->ud_dp->dl_unitdata.dl_dest_addr_length ? (uchar_t*) this->ud_dp->dl_dest_addr : NULL); this->op = this->arp_dp ? (uint32_t)((arh_t *) this->arp_dp)->arh_operation[1] : OP_UNKNOWN; /* hlen == 0 for UnARP requests */ this->hlen = this->arp_dp && this->arp_sz >= ARH_FIXED_LEN ? ((arh_t *) this->arp_dp)->arh_hlen : ETHERADDRL; this->tmp = this->eth_sea_p ? this->eth_sea_p : (uchar_t *) NULLSTR; MAC2STR(this->tmp, this->eth_sea); this->tmp = this->eth_dea_p ? this->eth_dea_p : (uchar_t *) NULLSTR; MAC2STR(this->tmp, this->eth_dea); this->offset = ARH_FIXED_LEN; this->arp_sea_p = this->arp_dp && this->arp_sz >= this->offset+this->hlen ? (uchar_t*) (this->arp_dp + this->offset) : NULL; this->offset += this->hlen; this->arp_sip_p = this->arp_dp && this->arp_sz >= this->offset+IPV4_ADDR_LEN ? (uchar_t*) (this->arp_dp + this->offset) : NULL; this->offset += IPV4_ADDR_LEN; this->arp_dea_p = this->arp_dp && this->arp_sz >= this->offset+this->hlen ? (uchar_t*) (this->arp_dp + this->offset) : NULL; this->offset += this->hlen; this->arp_dip_p = this->arp_dp && this->arp_sz >= this->offset+IPV4_ADDR_LEN ? (uchar_t*) (this->arp_dp + this->offset) : NULL; this->tmp = this->arp_sea_p ? this->arp_sea_p : (uchar_t *) NULLSTR; MAC2STR(this->tmp, this->arp_sea); this->tmp = this->arp_dea_p ? this->arp_dea_p : (uchar_t *) NULLSTR; MAC2STR(this->tmp, this->arp_dea); this->tmp = this->arp_sip_p ? this->arp_sip_p : (uchar_t *) NULLSTR; IP2STR(this->tmp, this->arp_sip); this->tmp = this->arp_dip_p ? this->arp_dip_p : (uchar_t *) NULLSTR; IP2STR(this->tmp, this->arp_dip); printf("%Y IN [%s%s]: Dropping %sfrom %s to %s (%s) - %s sender=%s/%s target=%s/%s\n", walltimestamp, (string) args[2]->ill_name, this->vlan ? strjoin(" VLAN#", lltostr(this->vlan)) : "", this->hlen == 0 ? "UnARP request " : this->arp_sip_p && this->arp_sip=="0.0.0.0" && OP[this->op][0]=="R" && this->arp_sea_p[0] && this->arp_sea != "00:00:00:00:00:00" ? "ARP probe " : OP[this->op][0]=="R" && this->arp_sip == this->arp_dip && this->arp_dip != "0.0.0.0" && this->arp_sea != "00:00:00:00:00:00" ? "ARP announcement " : this->arp_sip == this->arp_dip && this->arp_dip != "0.0.0.0" ? "gratuitous ARP " : OP[this->op][1], this->eth_sea_p ? (string) this->eth_sea : "???", this->eth_dea_p ? (string) this->eth_dea : "???", (string) args[0], /* ARP request data */ OP[this->op][0], this->arp_sea_p ? (string) this->arp_sea : "???", this->arp_sip_p ? (string) this->arp_sip : "???", this->arp_dea_p ? (string) this->arp_dea : "???", this->arp_dip_p ? (string) this->arp_dip : "???" ); } #endif