📄 pcap-linux.c
字号:
/* * pcap-linux.c: Packet capture interface to the Linux kernel * * Copyright (c) 2000 Torsten Landschoff <torsten@debian.org> * Sebastian Krahmer <krahmer@cs.uni-potsdam.de> * * License: BSD * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#ifndef lintstatic const char rcsid[] _U_ = "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.110.2.14 2006/10/12 17:26:58 guy Exp $ (LBL)";#endif/* * Known problems with 2.0[.x] kernels: * * - The loopback device gives every packet twice; on 2.2[.x] kernels, * if we use PF_PACKET, we can filter out the transmitted version * of the packet by using data in the "sockaddr_ll" returned by * "recvfrom()", but, on 2.0[.x] kernels, we have to use * PF_INET/SOCK_PACKET, which means "recvfrom()" supplies a * "sockaddr_pkt" which doesn't give us enough information to let * us do that. * * - We have to set the interface's IFF_PROMISC flag ourselves, if * we're to run in promiscuous mode, which means we have to turn * it off ourselves when we're done; the kernel doesn't keep track * of how many sockets are listening promiscuously, which means * it won't get turned off automatically when no sockets are * listening promiscuously. We catch "pcap_close()" and, for * interfaces we put into promiscuous mode, take them out of * promiscuous mode - which isn't necessarily the right thing to * do, if another socket also requested promiscuous mode between * the time when we opened the socket and the time when we close * the socket. * * - MSG_TRUNC isn't supported, so you can't specify that "recvfrom()" * return the amount of data that you could have read, rather than * the amount that was returned, so we can't just allocate a buffer * whose size is the snapshot length and pass the snapshot length * as the byte count, and also pass MSG_TRUNC, so that the return * value tells us how long the packet was on the wire. * * This means that, if we want to get the actual size of the packet, * so we can return it in the "len" field of the packet header, * we have to read the entire packet, not just the part that fits * within the snapshot length, and thus waste CPU time copying data * from the kernel that our caller won't see. * * We have to get the actual size, and supply it in "len", because * otherwise, the IP dissector in tcpdump, for example, will complain * about "truncated-ip", as the packet will appear to have been * shorter, on the wire, than the IP header said it should have been. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "pcap-int.h"#include "sll.h"#ifdef HAVE_DAG_API#include "pcap-dag.h"#endif /* HAVE_DAG_API */#ifdef HAVE_SEPTEL_API#include "pcap-septel.h"#endif /* HAVE_SEPTEL_API */ #include <errno.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/utsname.h>#include <net/if.h>#include <netinet/in.h>#include <linux/if_ether.h>#include <net/if_arp.h>/* * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET * sockets rather than SOCK_PACKET sockets. * * To use them, we include <linux/if_packet.h> rather than * <netpacket/packet.h>; we do so because * * some Linux distributions (e.g., Slackware 4.0) have 2.2 or * later kernels and libc5, and don't provide a <netpacket/packet.h> * file; * * not all versions of glibc2 have a <netpacket/packet.h> file * that defines stuff needed for some of the 2.4-or-later-kernel * features, so if the system has a 2.4 or later kernel, we * still can't use those features. * * We're already including a number of other <linux/XXX.h> headers, and * this code is Linux-specific (no other OS has PF_PACKET sockets as * a raw packet capture mechanism), so it's not as if you gain any * useful portability by using <netpacket/packet.h> * * XXX - should we just include <linux/if_packet.h> even if PF_PACKET * isn't defined? It only defines one data structure in 2.0.x, so * it shouldn't cause any problems. */#ifdef PF_PACKET# include <linux/if_packet.h> /* * On at least some Linux distributions (for example, Red Hat 5.2), * there's no <netpacket/packet.h> file, but PF_PACKET is defined if * you include <sys/socket.h>, but <linux/if_packet.h> doesn't define * any of the PF_PACKET stuff such as "struct sockaddr_ll" or any of * the PACKET_xxx stuff. * * So we check whether PACKET_HOST is defined, and assume that we have * PF_PACKET sockets only if it is defined. */# ifdef PACKET_HOST# define HAVE_PF_PACKET_SOCKETS# endif /* PACKET_HOST */#endif /* PF_PACKET */#ifdef SO_ATTACH_FILTER#include <linux/types.h>#include <linux/filter.h>#endif#ifndef __GLIBC__typedef int socklen_t;#endif#ifndef MSG_TRUNC/* * This is being compiled on a system that lacks MSG_TRUNC; define it * with the value it has in the 2.2 and later kernels, so that, on * those kernels, when we pass it in the flags argument to "recvfrom()" * we're passing the right value and thus get the MSG_TRUNC behavior * we want. (We don't get that behavior on 2.0[.x] kernels, because * they didn't support MSG_TRUNC.) */#define MSG_TRUNC 0x20#endif#ifndef SOL_PACKET/* * This is being compiled on a system that lacks SOL_PACKET; define it * with the value it has in the 2.2 and later kernels, so that we can * set promiscuous mode in the good modern way rather than the old * 2.0-kernel crappy way. */#define SOL_PACKET 263#endif#define MAX_LINKHEADER_SIZE 256/* * When capturing on all interfaces we use this as the buffer size. * Should be bigger then all MTUs that occur in real life. * 64kB should be enough for now. */#define BIGGER_THAN_ALL_MTUS (64*1024)/* * Prototypes for internal functions */static void map_arphrd_to_dlt(pcap_t *, int, int);static int live_open_old(pcap_t *, const char *, int, int, char *);static int live_open_new(pcap_t *, const char *, int, int, char *);static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *);static int pcap_read_packet(pcap_t *, pcap_handler, u_char *);static int pcap_inject_linux(pcap_t *, const void *, size_t);static int pcap_stats_linux(pcap_t *, struct pcap_stat *);static int pcap_setfilter_linux(pcap_t *, struct bpf_program *);static int pcap_setdirection_linux(pcap_t *, pcap_direction_t);static void pcap_close_linux(pcap_t *);/* * Wrap some ioctl calls */#ifdef HAVE_PF_PACKET_SOCKETSstatic int iface_get_id(int fd, const char *device, char *ebuf);#endifstatic int iface_get_mtu(int fd, const char *device, char *ebuf);static int iface_get_arptype(int fd, const char *device, char *ebuf);#ifdef HAVE_PF_PACKET_SOCKETSstatic int iface_bind(int fd, int ifindex, char *ebuf);#endifstatic int iface_bind_old(int fd, const char *device, char *ebuf);#ifdef SO_ATTACH_FILTERstatic int fix_program(pcap_t *handle, struct sock_fprog *fcode);static int fix_offset(struct bpf_insn *p);static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode);static int reset_kernel_filter(pcap_t *handle);static struct sock_filter total_insn = BPF_STMT(BPF_RET | BPF_K, 0);static struct sock_fprog total_fcode = { 1, &total_insn };#endif/* * Get a handle for a live capture from the given device. You can * pass NULL as device to get all packages (without link level * information of course). If you pass 1 as promisc the interface * will be set to promiscous mode (XXX: I think this usage should * be deprecated and functions be added to select that later allow * modification of that values -- Torsten). * * See also pcap(3). */pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf){ pcap_t *handle; int mtu; int err; int live_open_ok = 0; struct utsname utsname;#ifdef HAVE_DAG_API if (strstr(device, "dag")) { return dag_open_live(device, snaplen, promisc, to_ms, ebuf); }#endif /* HAVE_DAG_API */#ifdef HAVE_SEPTEL_API if (strstr(device, "septel")) { return septel_open_live(device, snaplen, promisc, to_ms, ebuf); }#endif /* HAVE_SEPTEL_API */ /* Allocate a handle for this session. */ handle = malloc(sizeof(*handle)); if (handle == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return NULL; } /* Initialize some components of the pcap structure. */ memset(handle, 0, sizeof(*handle)); handle->snapshot = snaplen; handle->md.timeout = to_ms; /* * NULL and "any" are special devices which give us the hint to * monitor all devices. */ if (!device || strcmp(device, "any") == 0) { device = NULL; handle->md.device = strdup("any"); if (promisc) { promisc = 0; /* Just a warning. */ snprintf(ebuf, PCAP_ERRBUF_SIZE, "Promiscuous mode not supported on the \"any\" device"); } } else handle->md.device = strdup(device); if (handle->md.device == NULL) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "strdup: %s", pcap_strerror(errno) ); free(handle); return NULL; } /* * Current Linux kernels use the protocol family PF_PACKET to * allow direct access to all packets on the network while * older kernels had a special socket type SOCK_PACKET to * implement this feature. * While this old implementation is kind of obsolete we need * to be compatible with older kernels for a while so we are * trying both methods with the newer method preferred. */ if ((err = live_open_new(handle, device, promisc, to_ms, ebuf)) == 1) live_open_ok = 1; else if (err == 0) { /* Non-fatal error; try old way */ if (live_open_old(handle, device, promisc, to_ms, ebuf)) live_open_ok = 1; } if (!live_open_ok) { /* * Both methods to open the packet socket failed. Tidy * up and report our failure (ebuf is expected to be * set by the functions above). */ if (handle->md.device != NULL) free(handle->md.device); free(handle); return NULL; } /* * Compute the buffer size. * * If we're using SOCK_PACKET, this might be a 2.0[.x] kernel, * and might require special handling - check. */ if (handle->md.sock_packet && (uname(&utsname) < 0 || strncmp(utsname.release, "2.0", 3) == 0)) { /* * We're using a SOCK_PACKET structure, and either * we couldn't find out what kernel release this is, * or it's a 2.0[.x] kernel. * * In the 2.0[.x] kernel, a "recvfrom()" on * a SOCK_PACKET socket, with MSG_TRUNC set, will * return the number of bytes read, so if we pass * a length based on the snapshot length, it'll * return the number of bytes from the packet * copied to userland, not the actual length * of the packet. * * This means that, for example, the IP dissector * in tcpdump will get handed a packet length less * than the length in the IP header, and will * complain about "truncated-ip". * * So we don't bother trying to copy from the * kernel only the bytes in which we're interested, * but instead copy them all, just as the older * versions of libpcap for Linux did. * * The buffer therefore needs to be big enough to * hold the largest packet we can get from this * device. Unfortunately, we can't get the MRU * of the network; we can only get the MTU. The * MTU may be too small, in which case a packet larger * than the buffer size will be truncated *and* we * won't get the actual packet size. * * However, if the snapshot length is larger than * the buffer size based on the MTU, we use the * snapshot length as the buffer size, instead; * this means that with a sufficiently large snapshot * length we won't artificially truncate packets * to the MTU-based size. * * This mess just one of many problems with packet * capture on 2.0[.x] kernels; you really want a * 2.2[.x] or later kernel if you want packet capture * to work well. */ mtu = iface_get_mtu(handle->fd, device, ebuf); if (mtu == -1) { pcap_close_linux(handle); free(handle); return NULL; } handle->bufsize = MAX_LINKHEADER_SIZE + mtu; if (handle->bufsize < handle->snapshot) handle->bufsize = handle->snapshot; } else { /* * This is a 2.2[.x] or later kernel (we know that * either because we're not using a SOCK_PACKET * socket - PF_PACKET is supported only in 2.2 * and later kernels - or because we checked the * kernel version). * * We can safely pass "recvfrom()" a byte count * based on the snapshot length. * * If we're in cooked mode, make the snapshot length * large enough to hold a "cooked mode" header plus * 1 byte of packet data (so we don't pass a byte * count of 0 to "recvfrom()"). */ if (handle->md.cooked) { if (handle->snapshot < SLL_HDR_LEN + 1) handle->snapshot = SLL_HDR_LEN + 1; } handle->bufsize = handle->snapshot; } /* Allocate the buffer */ handle->buffer = malloc(handle->bufsize + handle->offset); if (!handle->buffer) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); pcap_close_linux(handle); free(handle); return NULL; } /* * "handle->fd" is a socket, so "select()" and "poll()" * should work on it. */ handle->selectable_fd = handle->fd; handle->read_op = pcap_read_linux; handle->inject_op = pcap_inject_linux; handle->setfilter_op = pcap_setfilter_linux; handle->setdirection_op = pcap_setdirection_linux; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; handle->stats_op = pcap_stats_linux; handle->close_op = pcap_close_linux; return handle;}/* * Read at most max_packets from the capture stream and call the callback * for each of them. Returns the number of packets handled or -1 if an * error occured. */static int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -