📄 linux.c
字号:
#include <unistd.h>#include <stdlib.h>#define __USE_XOPEN#include <sys/poll.h>#include <sys/uio.h>#include <sys/socket.h>#include <sys/mman.h>#include <sys/ioctl.h>#include <features.h> /* for the glibc version number */#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1#include <netpacket/packet.h>#include <net/ethernet.h> /* the L2 protocols */#else#include <asm/types.h>#include <linux/if_packet.h>#include <linux/if_ether.h> /* The L2 protocols */#endif#include <linux/if.h>#include <string.h>#include <netinet/in.h>#include <asm/system.h>#include <firestorm.h>#include <plugin.h>#include <packet.h>#include <cleanup.h>#include <args.h>#include <alert.h>#include <signature.h>#include <decode.h>#include <capture.h>#include <args.h>PLUGIN_STD_DEFS();static int cb_ifname(struct arg *, void *);unsigned int nr_blocks;unsigned int force_mtu;char if_name[IFNAMSIZ];struct arg linux_args[]={ {"blocks",ARGTYPE_PUINT, NULL, {vp_uint:&nr_blocks}}, {"if",ARGTYPE_STRING,cb_ifname}, {"mtu",ARGTYPE_PBYTES, NULL, {vp_uint:&force_mtu}}, {NULL,ARGTYPE_NOP,NULL}};proc_decode_proto decode_proto;proc_serial_number serial_number;proc_args_parse args_parse;struct proto *sllproto=NULL;/* This is our own private data */struct lnx_priv { int fd; char *map; struct tpacket_req req; struct iovec *ring; size_t maplen; int mtu; int ifindex;};static int cb_ifname(struct arg *a, void *priv){ strncpy(if_name, a->val.v_str, sizeof(if_name)); return 1;}/* Bind us to an interface by index */static int linux_bindif(int fd, int ifindex){ struct sockaddr_ll addr; /* bind the packet socket */ memset((char *)&addr, 0, sizeof(addr)); addr.sll_family=AF_PACKET; addr.sll_protocol=__constant_htons(0x03); addr.sll_ifindex=ifindex; addr.sll_hatype=0; addr.sll_pkttype=0; addr.sll_halen=0; if ( bind(fd, (struct sockaddr *)&addr, sizeof(addr)) ) { return 0; } return 1;}/* This builds a ring buffer request structure making sure * that we have buffers big enough so that a frame which * is the size of the MTU doesn't get truncated. We also * need to structure things with minimum memory wastage */static void linux_mkreq(struct lnx_priv *p){ unsigned int pg,bs; unsigned int siz; unsigned int mult=1; bs=pg=getpagesize(); siz=p->mtu+TPACKET_HDRLEN; if ( bs <= siz ) { while(bs<siz) { bs+=pg; mult++; } p->req.tp_block_size=bs; p->req.tp_frame_size=bs/mult; p->req.tp_block_nr=nr_blocks; p->req.tp_frame_nr=mult*nr_blocks; }else{ while( (siz*(mult+1))<=pg ) { mult++; } p->req.tp_block_size=pg; p->req.tp_frame_size=pg/mult; p->req.tp_block_nr=nr_blocks; p->req.tp_frame_nr=mult*nr_blocks; }}/* Bind the packet socket to a specific interface, * we need to lookup the ifindex from the name * and we also need to determine the MTU */static int linux_iface(struct lnx_priv *p, char *ifn){ struct ifreq ifr; strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name)); /* Lookup the MTU */ if (ioctl(p->fd, SIOCGIFMTU, &ifr) < 0) { goto out_err; }else{ p->mtu=ifr.ifr_ifru.ifru_mtu; } /* Lookup the ifindex */ if (ioctl(p->fd, SIOCGIFINDEX, &ifr) < 0) { goto out_err; }else{ p->ifindex=ifr.ifr_ifru.ifru_ivalue; } /* Actually join up to the interface */ if ( linux_bindif(p->fd, p->ifindex) ) { return 1; }else{ mesg(M_ERR,"linux: %s: bind(): %s", ifn, get_err()); return 0; }out_err: mesg(M_ERR,"linux: %s: ioctl(): %s", ifn, get_err()); return 0;}/* Initialise a capture process, we open the file and then * return our opaque private data structure to firestorm */static void *lnx_init(char *args){ struct lnx_priv *p=malloc(sizeof(struct lnx_priv)); int i; if (!p) return NULL; if ( !sllproto && !(sllproto=decode_proto("linux")) ) { free(p); return NULL; } /* Open the packet socket */ if ( (p->fd=socket(PF_PACKET, SOCK_DGRAM, 0))<0 ) { mesg(M_ERR,"linux: socket(): %s", get_err()); free(p); return NULL; } /* Parse args */ if_name[0]=0; nr_blocks=128; force_mtu=0; if ( args ) { if ( args_parse(linux_args, args, NULL)<=0 ) { mesg(M_ERR,"linux: parse error: %s", args); free(p); return NULL; } } /* Join interfaces */ if ( !if_name || !strcmp(if_name, "any") ) { p->ifindex=0; /* zero == all */ if ( !linux_bindif(p->fd, p->ifindex) ) { mesg(M_ERR,"linux: %s: bind(): %s", if_name, get_err()); close(p->fd); free(p); return NULL; } /* TODO: enumerate all interfaces for * highest possible MTU */ if ( !force_mtu ) p->mtu=2048-TPACKET_HDRLEN; else p->mtu=force_mtu; }else{ if ( force_mtu ) { mesg(M_WARN,"linux: You are already specifying an interface " "no need to force MTU!"); } if ( !linux_iface(p, if_name) ) { close(p->fd); free(p); return NULL; } } /* Setup socket for RX ring buffer */ linux_mkreq(p); if ( (i=setsockopt(p->fd,SOL_PACKET,PACKET_RX_RING, (char *)&p->req, sizeof(p->req))) ) { mesg(M_ERR,"linux: setsockopt(): %s", get_err()); if ( i==-ENOPROTOOPT ) { mesg(M_ERR, "linux: you need CONFIG_PACKET_MMAP in your kernel"); } close(p->fd); free(p); return NULL; } /* mmap() the sucker */ p->maplen=p->req.tp_block_size * p->req.tp_block_nr; if ( (p->map=mmap(NULL, p->maplen, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, p->fd, 0))==MAP_FAILED ) { mesg(M_ERR,"linux: mmap(): %s", get_err()); close(p->fd); free(p); return NULL; } /* Allocate ring buffer iovecs */ if ( !(p->ring=malloc(p->maplen)) ) { mesg(M_ERR,"linux: malloc(): %s", get_err()); munmap(p->map, p->maplen); close(p->fd); free(p); return NULL; } /* Setup our ring buffer */ for(i=0; i<p->req.tp_frame_nr; i++) { p->ring[i].iov_base=(void *)(p->map+(i*p->req.tp_frame_size)); p->ring[i].iov_len=p->req.tp_frame_size; } /* Print information */ mesg(M_INFO,"linux: %s(%i): mtu=%i blocks=%u buffer=%uKB (%u frames)", if_name, p->ifindex, p->mtu, nr_blocks, (p->req.tp_block_size*p->req.tp_block_nr)/1024, p->req.tp_frame_nr); return p;}static void lnx_end(void *priv){ struct lnx_priv *p=priv; struct tpacket_stats st; int len=sizeof(st); if ( !p ) return; /* Print statistics */ if ( !getsockopt(p->fd, SOL_PACKET, PACKET_STATISTICS, (char *)&st, &len)) { mesg(M_INFO,"linux: received %u packets, dropped %u", st.tp_packets, st.tp_drops); } munmap(p->map, p->maplen); close(p->fd); free(p);}static void lnx_go(void *priv, struct capture *c){ struct lnx_priv *p=priv; struct tpacket_hdr *h; struct sockaddr_ll *sll; struct pollfd pfd; struct packet pkt; static int i=0; memset(&pkt, 0, sizeof(pkt)); pkt.capture=c; while ( c->state==CAP_STATE_CAPTURE ) { /* If state gets changed during this while loop * we will not see it untill the next poll... It's a * corner case really. */ while(*(unsigned long*)p->ring[i].iov_base) { h=p->ring[i].iov_base; sll=(void *)h + TPACKET_ALIGN(sizeof(*h)); /* build packet */ pkt.time.tv_sec=h->tp_sec; pkt.time.tv_usec=h->tp_usec; pkt.len=h->tp_mac+h->tp_len; pkt.caplen=h->tp_mac+h->tp_snaplen; pkt.base=h; pkt.end=(void *)h+pkt.caplen; /* Decode */ pkt.layer[0].proto=sllproto; pkt.layer[0].h.raw=sll; pkt.layer[0].flags=0; pkt.layer[0].session=NULL; pkt.llen=0; pkt.flags=FP_CLONE|FP_LIVE|FP_PKTTYPE; switch ( sll->sll_pkttype ) { case 0: pkt.flags|=FP_HOST; break; case 1: pkt.flags|=FP_BROADCAST; break; case 2: pkt.flags|=FP_MULTICAST; break; case 4: pkt.flags|=FP_OUTGOING; break; case 3: default: pkt.flags|=FP_PROMISC; break; } /* Dispatch */ serial_number(&pkt.serial); sllproto->decode(&pkt); /* move along */ h->tp_status=0; mb(); i=(i>=p->req.tp_frame_nr-1) ? 0 : i+1; } pfd.fd=p->fd; pfd.events=POLLIN|POLLERR; pfd.revents=0; if ( poll(&pfd, 1, -1)<0 ) { if ( errno==EINTR ) return; mesg(M_CRIT, "linux: poll(): %s", get_err()); c->state=CAP_STATE_STOP; return; } if ( pfd.revents & (POLLERR|POLLHUP) ) { mesg(M_CRIT, "linux: %s", get_err()); c->state=CAP_STATE_STOP; return; } }}static struct capdev linux_cap={ .name="linux", .init=lnx_init, .end=lnx_end, .go=lnx_go,};int PLUGIN_CAPDEV (struct capture_api *c){ object_check(c); serial_number=c->serial_number; decode_proto=c->decode_proto; args_parse=c->args_parse; if ( !c->capdev_add(&linux_cap) ) return PLUGIN_ERR_FAIL; return PLUGIN_ERR_OK;}int PLUGIN_INIT (struct plugin_in *in, struct plugin_out *out){ /* validate input */ plugin_check(in, out); PLUGIN_ID("capture.linux", "Linux mmap() packet socket"); PLUGIN_VERSION(1, 0); PLUGIN_AUTHOR("Gianni Tedesco", "gianni@scaramanga.co.uk"); PLUGIN_LICENSE("GPL"); return PLUGIN_ERR_OK;}int PLUGIN_UNLOAD (int code) { return PLUGIN_ERR_OK;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -