mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
Nsock-pcap cleanup
Make the API follow the general nsock style. Report errors properly using the nsock logging facilities.
This commit is contained in:
@@ -389,8 +389,8 @@ int FPNetworkControl::unregister_caller(FPHost *oldcaller) {
|
||||
/* This method gets the controller ready for packet capture. Basically it
|
||||
* obtains a pcap descriptor from nsock and sets an appropriate BPF filter. */
|
||||
int FPNetworkControl::setup_sniffer(const char *iface, const char *bpf_filter) {
|
||||
char *errmsg = NULL;
|
||||
char pcapdev[128];
|
||||
int rc;
|
||||
|
||||
#ifdef WIN32
|
||||
/* Nmap normally uses device names obtained through dnet for interfaces, but
|
||||
@@ -405,8 +405,9 @@ int FPNetworkControl::setup_sniffer(const char *iface, const char *bpf_filter) {
|
||||
#endif
|
||||
|
||||
/* Obtain a pcap descriptor */
|
||||
if ((errmsg = nsock_pcap_open(this->nsp, this->pcap_nsi, pcapdev, 8192, 0, bpf_filter)) != NULL)
|
||||
fatal("Error opening capture device %s --> %s\n", pcapdev, errmsg);
|
||||
rc = nsock_pcap_open(this->nsp, this->pcap_nsi, pcapdev, 8192, 0, bpf_filter);
|
||||
if (rc)
|
||||
fatal("Error opening capture device %s\n", pcapdev);
|
||||
|
||||
/* Store the pcap NSI inside the pool so we can retrieve it inside a callback */
|
||||
nsp_setud(this->nsp, (void *)&(this->pcap_nsi));
|
||||
|
||||
@@ -1435,7 +1435,6 @@ int EchoServer::start() {
|
||||
nsock_iod client_nsi; /**< Stores connected client IOD */
|
||||
nsock_iod pcap_nsi; /**< Stores Pcap IOD */
|
||||
char pcapdev[128]; /**< Device name passed to pcap_open_live */
|
||||
char *auxpnt=NULL; /**< Aux str pointer */
|
||||
struct timeval now; /**< For timestamps */
|
||||
struct sockaddr_storage ss; /**< New client socket address */
|
||||
socklen_t sslen=sizeof(ss); /**< New client socket address len */
|
||||
@@ -1444,6 +1443,7 @@ int EchoServer::start() {
|
||||
clientid_t *idpnt=NULL; /**< For new client assigned identifiers */
|
||||
NEPContext ctx; /**< Context for the new client */
|
||||
EchoHeader h;
|
||||
int rc;
|
||||
|
||||
/* Create a new nsock pool */
|
||||
if ((nsp = nsp_new(NULL)) == NULL)
|
||||
@@ -1463,8 +1463,10 @@ int EchoServer::start() {
|
||||
/* Open pcap */
|
||||
nping_print(DBG_2,"Opening pcap device %s", o.getDevice());
|
||||
Strncpy(pcapdev, o.getDevice(), sizeof(pcapdev));
|
||||
if( (auxpnt=nsock_pcap_open(nsp, pcap_nsi, pcapdev, MAX_ECHOED_PACKET_LEN, 1, ProbeMode::getBPFFilterString() )) != NULL )
|
||||
nping_fatal(QT_3, "Error opening capture device %s --> %s\n", o.getDevice(), auxpnt);
|
||||
rc = nsock_pcap_open(nsp, pcap_nsi, pcapdev, MAX_ECHOED_PACKET_LEN, 1,
|
||||
ProbeMode::getBPFFilterString());
|
||||
if (rc)
|
||||
nping_fatal(QT_3, "Error opening capture device %s\n", o.getDevice());
|
||||
else
|
||||
nping_print(VB_0,"Packet capture will be performed using network interface %s.", o.getDevice());
|
||||
nping_print(VB_0,"Waiting for connections...");
|
||||
|
||||
@@ -199,7 +199,7 @@ nsock_pool ProbeMode::getNsockPool(){
|
||||
* event handlers, here we just schedule them, take care of the timers,
|
||||
* set up pcap and the bpf filter, etc. */
|
||||
int ProbeMode::start(){
|
||||
|
||||
int rc;
|
||||
int p=0, pc=-1; /**< Indexes for ports count */
|
||||
u32 c=0; /**< Index for packet count */
|
||||
u32 zero=0; /**< Empty payload */
|
||||
@@ -211,7 +211,6 @@ int ProbeMode::start(){
|
||||
int numTargetPorts=0; /**< Total number of target ports */
|
||||
u16 currentPort=0; /**< Current target port */
|
||||
char *filterstring; /**< Stores BFP filter spec string */
|
||||
char *auxpnt=NULL; /**< Aux str pointer */
|
||||
int rawipsd=-1; /**< Descriptor for raw IP socket */
|
||||
enum nsock_loopstatus loopret; /**< Stores nsock_loop returned status */
|
||||
nsock_iod pcap_nsi; /**< Stores Pcap IOD */
|
||||
@@ -376,17 +375,21 @@ int ProbeMode::start(){
|
||||
filterstring=getBPFFilterString();
|
||||
nping_print(DBG_2,"Opening pcap device %s", o.getDevice() );
|
||||
#ifdef WIN32
|
||||
/* Nping normally uses device names obtained through dnet for interfaces, but Pcap has its own
|
||||
naming system. So the conversion is done here */
|
||||
/* Nping normally uses device names obtained through dnet for interfaces,
|
||||
* but Pcap has its own naming system. So the conversion is done here */
|
||||
if (!DnetName2PcapName(o.getDevice(), pcapdev, sizeof(pcapdev))) {
|
||||
/* Oh crap -- couldn't find the corresponding dev apparently. Let's just go with what we have then ... */
|
||||
/* Oh crap -- couldn't find the corresponding dev apparently.
|
||||
* Let's just go with what we have then ... */
|
||||
Strncpy(pcapdev, o.getDevice(), sizeof(pcapdev));
|
||||
}
|
||||
#else
|
||||
Strncpy(pcapdev, o.getDevice(), sizeof(pcapdev));
|
||||
#endif
|
||||
if( (auxpnt=nsock_pcap_open(nsp, pcap_nsi, pcapdev, 8192, (o.spoofSource())? 1 : 0, filterstring )) != NULL )
|
||||
nping_fatal(QT_3, "Error opening capture device %s --> %s\n", o.getDevice(), auxpnt);
|
||||
|
||||
rc = nsock_pcap_open(nsp, pcap_nsi, pcapdev, 8192,
|
||||
(o.spoofSource()) ? 1 : 0, filterstring);
|
||||
if (rc)
|
||||
nping_fatal(QT_3, "Error opening capture device %s\n", o.getDevice());
|
||||
nping_print(DBG_2,"Pcap device %s open successfully", o.getDevice());
|
||||
}
|
||||
|
||||
|
||||
@@ -962,6 +962,8 @@ static int l_pcap_open (lua_State *L)
|
||||
nsock_iod *nsiod = (nsock_iod *) lua_touserdata(L, -1);
|
||||
if (nsiod == NULL) /* does not exist */
|
||||
{
|
||||
int rc;
|
||||
|
||||
lua_pop(L, 1); /* the nonexistant socket */
|
||||
nsiod = (nsock_iod *) lua_newuserdata(L, sizeof(nsock_iod));
|
||||
lua_pushvalue(L, PCAP_SOCKET);
|
||||
@@ -970,10 +972,10 @@ static int l_pcap_open (lua_State *L)
|
||||
lua_pushvalue(L, 7); /* the pcap socket key */
|
||||
lua_pushvalue(L, -2); /* the pcap socket nsiod */
|
||||
lua_rawset(L, KEY_PCAP); /* KEY_PCAP["dev|snap|promis|bpf"] = pcap_nsiod */
|
||||
char *e = nsock_pcap_open(nsp, *nsiod, lua_tostring(L, 6), snaplen,
|
||||
rc = nsock_pcap_open(nsp, *nsiod, lua_tostring(L, 6), snaplen,
|
||||
lua_toboolean(L, 4), bpf);
|
||||
if (e)
|
||||
luaL_error(L, "%s", e);
|
||||
if (rc)
|
||||
luaL_error(L, "can't open pcap reader on %s", device);
|
||||
}
|
||||
lua_getuservalue(L, 1); /* the socket user value */
|
||||
lua_pushvalue(L, -2); /* the pcap socket nsiod */
|
||||
|
||||
@@ -622,13 +622,16 @@ const struct timeval *nsock_gettimeofday();
|
||||
* promisc: whether to open device in promiscuous mode
|
||||
* bpf_fmt: berkeley filter
|
||||
*
|
||||
* return value: NULL if everything was okay, or error string if error occurred
|
||||
* [sorry Fyodor for breaking the API, but it's just simpler]
|
||||
* return value: 0 if everything was okay, or error code if error occurred.
|
||||
* */
|
||||
char *nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, const char *pcap_device, int snaplen, int promisc, const char *bpf_fmt, ...);
|
||||
int nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, const char *pcap_device,
|
||||
int snaplen, int promisc, const char *bpf_fmt, ...);
|
||||
|
||||
/* Requests exactly one packet to be captured.from pcap.See nsock_read() for parameters description. */
|
||||
nsock_event_id nsock_pcap_read_packet(nsock_pool nsp, nsock_iod nsiod, nsock_ev_handler handler, int timeout_msecs, void *userdata);
|
||||
/* Requests exactly one packet to be captured.from pcap.
|
||||
* See nsock_read() for parameters description. */
|
||||
nsock_event_id nsock_pcap_read_packet(nsock_pool nsp, nsock_iod nsiod,
|
||||
nsock_ev_handler handler,
|
||||
int timeout_msecs, void *userdata);
|
||||
|
||||
/* Gets packet data. This should be called after successful receipt of packet
|
||||
* to get packet. If you're not interested in some values, just pass NULL
|
||||
@@ -640,7 +643,8 @@ nsock_event_id nsock_pcap_read_packet(nsock_pool nsp, nsock_iod nsiod, nsock_ev_
|
||||
* As a result you'll get longer times than you should, but it's safer to
|
||||
* think that host is a bit further.
|
||||
* */
|
||||
void nse_readpcap(nsock_event nsee, const unsigned char **l2_data, size_t *l2_len, const unsigned char **l3_data, size_t *l3_len,
|
||||
void nse_readpcap(nsock_event nsee, const unsigned char **l2_data,
|
||||
size_t *l2_len, const unsigned char **l3_data, size_t *l3_len,
|
||||
size_t *packet_len, struct timeval *ts);
|
||||
|
||||
/* Well. Just pcap-style datalink. Like DLT_EN10MB or DLT_SLIP. Check in pcap(3) manpage. */
|
||||
|
||||
@@ -81,192 +81,50 @@
|
||||
extern struct timeval nsock_tod;
|
||||
|
||||
#if HAVE_PCAP
|
||||
static int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl);
|
||||
static char * nsock_pcap_set_filter(pcap_t *pt, const char *device, const char *bpf);
|
||||
|
||||
/* Convert new nsiod to pcap descriptor. Other parameters have the same meaning
|
||||
* as for pcap_open_live in pcap(3).
|
||||
* device : pcap-style device name
|
||||
* snaplen : size of packet to be copied to hanler
|
||||
* promisc : whether to open device in promiscuous mode
|
||||
* bpf_fmt : berkeley filter
|
||||
* return value: NULL if everything was okay, or error string if error occurred. */
|
||||
char* nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, const char *pcap_device, int snaplen,
|
||||
int promisc, const char *bpf_fmt, ...) {
|
||||
msiod *nsi = (msiod *)nsiod;
|
||||
mspool *ms = (mspool *)nsp;
|
||||
mspcap *mp = (mspcap *)nsi->pcap;
|
||||
static char errorbuf[128];
|
||||
char err0r[PCAP_ERRBUF_SIZE];
|
||||
/* packet filter string */
|
||||
char bpf[4096];
|
||||
va_list ap;
|
||||
int failed, datalink;
|
||||
char *e;
|
||||
#define PCAP_OPEN_MAX_RETRIES 3
|
||||
|
||||
#ifdef PCAP_CAN_DO_SELECT
|
||||
#if PCAP_BSD_SELECT_HACK
|
||||
/* MacOsX reports error if to_ms is too big (like INT_MAX) with error
|
||||
* FAILED. Reported error: BIOCSRTIMEOUT: Invalid argument
|
||||
* INT_MAX/6 (=357913941) seems to be working... */
|
||||
int to_ms = 357913941;
|
||||
#else
|
||||
int to_ms = 200;
|
||||
#endif /* PCAP_BSD_SELECT_HACK */
|
||||
#define PCAP_FAILURE_EXPL_MESSAGE \
|
||||
"There are several possible reasons for this, " \
|
||||
"depending on your operating system:\n" \
|
||||
"LINUX: If you are getting Socket type not supported, " \
|
||||
"try modprobe af_packet or recompile your kernel with PACKET enabled.\n" \
|
||||
"*BSD: If you are getting device not configured, you need to recompile " \
|
||||
"your kernel with Berkeley Packet Filter support." \
|
||||
"If you are getting No such file or directory, try creating the device " \
|
||||
"(eg cd /dev; MAKEDEV <device>; or use mknod).\n" \
|
||||
"*WINDOWS: Nmap only supports ethernet interfaces on Windows for most " \
|
||||
"operations because Microsoft disabled raw sockets as of Windows XP SP2. " \
|
||||
"Depending on the reason for this error, it is possible that the " \
|
||||
"--unprivileged command-line argument will help.\n" \
|
||||
"SOLARIS: If you are trying to scan localhost and getting "\
|
||||
"'/dev/lo0: No such file or directory', complain to Sun. "\
|
||||
"I don't think Solaris can support advanced localhost scans. "\
|
||||
"You can probably use \"-PN -sT localhost\" though.\n\n"
|
||||
|
||||
#else
|
||||
int to_ms = 1;
|
||||
#endif
|
||||
|
||||
gettimeofday(&nsock_tod, NULL);
|
||||
|
||||
if (mp)
|
||||
return "nsock-pcap: this nsi already has pcap device opened";
|
||||
|
||||
mp = (mspcap *)safe_zalloc(sizeof(mspcap));
|
||||
nsi->pcap = (void *)mp;
|
||||
|
||||
va_start(ap, bpf_fmt);
|
||||
if (Vsnprintf(bpf, sizeof(bpf), bpf_fmt, ap) >= (int)sizeof(bpf)) {
|
||||
va_end(ap);
|
||||
return "nsock-pcap: nsock_pcap_open called with too-large bpf filter arg";
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
nsock_log_info(ms,
|
||||
"PCAP requested on device '%s' with berkeley filter '%s' (promisc=%i snaplen=%i to_ms=%i) (IOD #%li)",
|
||||
pcap_device,bpf, promisc, snaplen, to_ms, nsi->id);
|
||||
|
||||
failed = 0;
|
||||
do {
|
||||
mp->pt = pcap_open_live((char *)pcap_device, snaplen, promisc, to_ms, err0r);
|
||||
if (mp->pt) /* okay, opened!*/
|
||||
break;
|
||||
|
||||
/* sorry, something failed*/
|
||||
if (++failed >= 3) {
|
||||
mp->pcap_device = strdup(pcap_device);
|
||||
fprintf(stderr,
|
||||
"Call to pcap_open_live(%s, %d, %d, %d) failed three times. Reported error: %s\n"
|
||||
"There are several possible reasons for this, depending on your operating system:\n"
|
||||
"LINUX: If you are getting Socket type not supported, try modprobe af_packet or recompile your kernel with PACKET enabled.\n"
|
||||
"*BSD: If you are getting device not configured, you need to recompile your kernel with Berkeley Packet Filter support. If you are getting No such file or directory, try creating the device (eg cd /dev; MAKEDEV <device>; or use mknod).\n"
|
||||
"*WINDOWS: Nmap only supports ethernet interfaces on Windows for most operations because Microsoft disabled raw sockets as of Windows XP SP2. Depending on the reason for this error, it is possible that the --unprivileged command-line argument will help.\n"
|
||||
"SOLARIS: If you are trying to scan localhost and getting '/dev/lo0: No such file or directory', complain to Sun. I don't think Solaris can support advanced localhost scans. You can probably use \"-PN -sT localhost\" though.\n\n",
|
||||
pcap_device, snaplen, promisc, to_ms, err0r);
|
||||
return "nsock-pcap: can't open pcap! Are you root?";
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s. Will wait %d seconds then retry.\n",
|
||||
pcap_device, snaplen, promisc, to_ms, err0r, 4*failed);
|
||||
sleep(4* failed);
|
||||
} while (1);
|
||||
|
||||
e = nsock_pcap_set_filter(mp->pt, pcap_device, bpf);
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
#ifdef WIN32
|
||||
/* We want any responses back ASAP */
|
||||
pcap_setmintocopy(mp->pt, 1);
|
||||
#endif
|
||||
|
||||
mp->l3_offset = nsock_pcap_get_l3_offset(mp->pt, &datalink);
|
||||
mp->snaplen = snaplen;
|
||||
mp->datalink = datalink;
|
||||
mp->pcap_device = strdup(pcap_device);
|
||||
#ifdef PCAP_CAN_DO_SELECT
|
||||
mp->pcap_desc = pcap_get_selectable_fd(mp->pt);
|
||||
#else
|
||||
mp->pcap_desc = -1;
|
||||
#endif
|
||||
mp->readsd_count = 0;
|
||||
|
||||
/* Without setting this ioctl, some systems (BSDs, though it depends on the
|
||||
* release) will buffer packets in non-blocking mode and only return them in a
|
||||
* bunch when the buffer is full. Setting the ioctl makes each one be
|
||||
* delivered immediately. This is how Linux works by default. See the comments
|
||||
* surrounding the setting of BIOCIMMEDIATE in libpcap/pcap-bpf.c. */
|
||||
#ifdef BIOCIMMEDIATE
|
||||
if (mp->pcap_desc != -1) {
|
||||
int immediate = 1;
|
||||
|
||||
if (ioctl(mp->pcap_desc, BIOCIMMEDIATE, &immediate) < 0)
|
||||
fatal("Cannot set BIOCIMMEDIATE on pcap descriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set device non-blocking */
|
||||
if (pcap_setnonblock(mp->pt, 1, err0r) < 0) {
|
||||
/* I can't do select() on pcap! blocking + no_select is fatal */
|
||||
if(mp->pcap_desc < 0){
|
||||
Snprintf(errorbuf, sizeof(errorbuf),
|
||||
"nsock-pcap: Failed to set pcap descriptor on device %s to nonblocking state: %s",
|
||||
pcap_device, err0r);
|
||||
return errorbuf;
|
||||
}
|
||||
|
||||
/* When we use bsd hack we also need to set non-blocking */
|
||||
#ifdef PCAP_BSD_SELECT_HACK
|
||||
Snprintf(errorbuf, sizeof(errorbuf),
|
||||
"nsock-pcap: Failed to set pcap descriptor on device %s to nonblocking state: %s",
|
||||
pcap_device, err0r);
|
||||
return errorbuf;
|
||||
#endif
|
||||
|
||||
/* in other case, we can accept blocking pcap */
|
||||
fprintf(stderr, "Failed to set pcap descriptor on device %s to nonblocking state: %s",
|
||||
pcap_device, err0r);
|
||||
}
|
||||
|
||||
if (ms->loglevel <= NSOCK_LOG_INFO) {
|
||||
#if PCAP_BSD_SELECT_HACK
|
||||
int bsd_select_hack = 1;
|
||||
#else
|
||||
int bsd_select_hack = 0;
|
||||
#endif
|
||||
|
||||
#if PCAP_RECV_TIMEVAL_VALID
|
||||
int recv_timeval_valid = 1;
|
||||
#else
|
||||
int recv_timeval_valid = 0;
|
||||
#endif
|
||||
|
||||
nsock_log_info(ms, "PCAP created successfully on device '%s'"
|
||||
" (pcap_desc=%i bsd_hack=%i to_valid=%i l3_offset=%i) (IOD #%li)",
|
||||
pcap_device,
|
||||
mp->pcap_desc,
|
||||
bsd_select_hack,
|
||||
recv_timeval_valid,
|
||||
mp->l3_offset,
|
||||
nsi->id);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *nsock_pcap_set_filter(pcap_t *pt, const char *device, const char *bpf) {
|
||||
static int nsock_pcap_set_filter(mspool *nsp, pcap_t *pt, const char *device,
|
||||
const char *bpf) {
|
||||
struct bpf_program fcode;
|
||||
static char errorbuf[128];
|
||||
int rc;
|
||||
|
||||
/* log_write(LOG_STDOUT, "Packet capture filter (device %s): %s\n", device, buf); */
|
||||
|
||||
if (pcap_compile(pt, &fcode, (char*)bpf, 1, 0) < 0) {
|
||||
Snprintf(errorbuf, sizeof(errorbuf), "Error compiling our pcap filter: %s\n", pcap_geterr(pt));
|
||||
return errorbuf;
|
||||
rc = pcap_compile(pt, &fcode, (char *)bpf, 1, 0);
|
||||
if (rc) {
|
||||
nsock_log_error(nsp, "Error compiling pcap filter: %s", pcap_geterr(pt));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pcap_setfilter(pt, &fcode) < 0 ) {
|
||||
Snprintf(errorbuf, sizeof(errorbuf),"Failed to set the pcap filter: %s\n", pcap_geterr(pt));
|
||||
return errorbuf;
|
||||
rc = pcap_setfilter(pt, &fcode);
|
||||
if (rc) {
|
||||
nsock_log_error(nsp, "Failed to set the pcap filter: %s", pcap_geterr(pt));
|
||||
return rc;
|
||||
}
|
||||
|
||||
pcap_freecode(&fcode);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl) {
|
||||
static int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl) {
|
||||
int datalink;
|
||||
unsigned int offset = 0;
|
||||
|
||||
@@ -275,7 +133,9 @@ int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl) {
|
||||
fatal("Cannot obtain datalink information: %s", pcap_geterr(pt));
|
||||
|
||||
/* XXX NOTE:
|
||||
* if a new offset ever exceeds the current max (24), adjust MAX_LINK_HEADERSZ in libnetutil/netutil.h */
|
||||
* if a new offset ever exceeds the current max (24),
|
||||
* adjust MAX_LINK_HEADERSZ in libnetutil/netutil.h
|
||||
*/
|
||||
switch (datalink) {
|
||||
case DLT_EN10MB: offset = 14; break;
|
||||
case DLT_IEEE802: offset = 22; break;
|
||||
@@ -338,9 +198,170 @@ int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl) {
|
||||
return (offset);
|
||||
}
|
||||
|
||||
static int nsock_pcap_try_open(mspool *nsp, mspcap *mp, const char *dev,
|
||||
int snaplen, int promisc, int timeout_ms,
|
||||
char *errbuf) {
|
||||
mp->pt = pcap_open_live(dev, snaplen, promisc, timeout_ms, errbuf);
|
||||
if (!mp->pt) {
|
||||
nsock_log_error(nsp, "pcap_open_live(%s, %d, %d, %d) failed with error: %s",
|
||||
dev, snaplen, promisc, timeout_ms, errbuf);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert new nsiod to pcap descriptor. Other parameters have
|
||||
* the same meaning as for pcap_open_live in pcap(3).
|
||||
* device : pcap-style device name
|
||||
* snaplen : size of packet to be copied to hanler
|
||||
* promisc : whether to open device in promiscuous mode
|
||||
* bpf_fmt : berkeley filter
|
||||
* return value: NULL if everything was okay, or error string
|
||||
* if error occurred. */
|
||||
int nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, const char *pcap_device,
|
||||
int snaplen, int promisc, const char *bpf_fmt, ...) {
|
||||
msiod *nsi = (msiod *)nsiod;
|
||||
mspool *ms = (mspool *)nsp;
|
||||
mspcap *mp = (mspcap *)nsi->pcap;
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
char bpf[4096];
|
||||
va_list ap;
|
||||
int failed, datalink;
|
||||
int rc;
|
||||
|
||||
#ifdef PCAP_CAN_DO_SELECT
|
||||
#if PCAP_BSD_SELECT_HACK
|
||||
/* MacOsX reports error if to_ms is too big (like INT_MAX) with error
|
||||
* FAILED. Reported error: BIOCSRTIMEOUT: Invalid argument
|
||||
* INT_MAX/6 (=357913941) seems to be working... */
|
||||
int to_ms = 357913941;
|
||||
#else
|
||||
int to_ms = 200;
|
||||
#endif /* PCAP_BSD_SELECT_HACK */
|
||||
|
||||
#else
|
||||
int to_ms = 1;
|
||||
#endif
|
||||
|
||||
gettimeofday(&nsock_tod, NULL);
|
||||
|
||||
if (mp) {
|
||||
nsock_log_error(ms, "This nsi already has pcap device opened");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mp = (mspcap *)safe_zalloc(sizeof(mspcap));
|
||||
nsi->pcap = (void *)mp;
|
||||
|
||||
va_start(ap, bpf_fmt);
|
||||
rc = Vsnprintf(bpf, sizeof(bpf), bpf_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (rc >= (int)sizeof(bpf)) {
|
||||
nsock_log_error(ms, "Too-large bpf filter argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsock_log_info(ms, "PCAP requested on device '%s' with berkeley filter '%s' "
|
||||
"(promisc=%i snaplen=%i to_ms=%i) (IOD #%li)",
|
||||
pcap_device,bpf, promisc, snaplen, to_ms, nsi->id);
|
||||
|
||||
failed = 0;
|
||||
do {
|
||||
rc = nsock_pcap_try_open(ms, mp, pcap_device, snaplen, promisc, to_ms, errbuf);
|
||||
if (rc) {
|
||||
failed++;
|
||||
nsock_log_error(ms, "Will wait %d seconds then retry.", 4 * failed);
|
||||
sleep(4 * failed);
|
||||
}
|
||||
} while (rc && failed < PCAP_OPEN_MAX_RETRIES);
|
||||
|
||||
if (rc) {
|
||||
nsock_log_error(ms, "pcap_open_live(%s, %d, %d, %d) failed %d times.",
|
||||
pcap_device, snaplen, promisc, to_ms, failed);
|
||||
nsock_log_error(ms, PCAP_FAILURE_EXPL_MESSAGE);
|
||||
nsock_log_error(ms, "Can't open pcap! Are you root?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = nsock_pcap_set_filter(ms, mp->pt, pcap_device, bpf);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
#ifdef WIN32
|
||||
/* We want any responses back ASAP */
|
||||
pcap_setmintocopy(mp->pt, 1);
|
||||
#endif
|
||||
|
||||
mp->l3_offset = nsock_pcap_get_l3_offset(mp->pt, &datalink);
|
||||
mp->snaplen = snaplen;
|
||||
mp->datalink = datalink;
|
||||
mp->pcap_device = strdup(pcap_device);
|
||||
#ifdef PCAP_CAN_DO_SELECT
|
||||
mp->pcap_desc = pcap_get_selectable_fd(mp->pt);
|
||||
#else
|
||||
mp->pcap_desc = -1;
|
||||
#endif
|
||||
mp->readsd_count = 0;
|
||||
|
||||
/* Without setting this ioctl, some systems (BSDs, though it depends on the
|
||||
* release) will buffer packets in non-blocking mode and only return them in a
|
||||
* bunch when the buffer is full. Setting the ioctl makes each one be
|
||||
* delivered immediately. This is how Linux works by default. See the comments
|
||||
* surrounding the setting of BIOCIMMEDIATE in libpcap/pcap-bpf.c. */
|
||||
#ifdef BIOCIMMEDIATE
|
||||
i f (mp->pcap_desc != -1) {
|
||||
int immediate = 1;
|
||||
|
||||
if (ioctl(mp->pcap_desc, BIOCIMMEDIATE, &immediate) < 0)
|
||||
fatal("Cannot set BIOCIMMEDIATE on pcap descriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set device non-blocking */
|
||||
rc = pcap_setnonblock(mp->pt, 1, errbuf);
|
||||
if (rc) {
|
||||
|
||||
/* I can't do select() on pcap!
|
||||
* blocking + no_select is fatal */
|
||||
#ifndef PCAP_BSD_SELECT_HACK
|
||||
if (mp->pcap_desc < 0)
|
||||
#endif
|
||||
{
|
||||
nsock_log_error(ms, "Failed to set pcap descriptor on device %s "
|
||||
"to nonblocking mode: %s", pcap_device, errbuf);
|
||||
return -1;
|
||||
}
|
||||
/* in other case, we can accept blocking pcap */
|
||||
nsock_log_info(ms, "Failed to set pcap descriptor on device %s "
|
||||
"to nonblocking state: %s", pcap_device, errbuf);
|
||||
}
|
||||
|
||||
if (ms->loglevel <= NSOCK_LOG_INFO) {
|
||||
#if PCAP_BSD_SELECT_HACK
|
||||
int bsd_select_hack = 1;
|
||||
#else
|
||||
int bsd_select_hack = 0;
|
||||
#endif
|
||||
|
||||
#if PCAP_RECV_TIMEVAL_VALID
|
||||
int recv_timeval_valid = 1;
|
||||
#else
|
||||
int recv_timeval_valid = 0;
|
||||
#endif
|
||||
|
||||
nsock_log_info(ms, "PCAP created successfully on device '%s' "
|
||||
"(pcap_desc=%i bsd_hack=%i to_valid=%i l3_offset=%i) (IOD #%li)",
|
||||
pcap_device, mp->pcap_desc, bsd_select_hack,
|
||||
recv_timeval_valid, mp->l3_offset, nsi->id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Requests exactly one packet to be captured. */
|
||||
nsock_event_id nsock_pcap_read_packet(nsock_pool nsp, nsock_iod nsiod,
|
||||
nsock_ev_handler handler, int timeout_msecs, void *userdata) {
|
||||
nsock_ev_handler handler,
|
||||
int timeout_msecs, void *userdata) {
|
||||
msiod *nsi = (msiod *)nsiod;
|
||||
mspool *ms = (mspool *)nsp;
|
||||
msevent *nse;
|
||||
@@ -377,7 +398,8 @@ int do_actual_pcap_read(msevent *nse) {
|
||||
#ifdef PCAP_RECV_TIMEVAL_VALID
|
||||
npp.ts = pkt_header->ts;
|
||||
#else
|
||||
/* on these platforms time received from pcap is invalid. It's better to set current time */
|
||||
/* On these platforms time received from pcap is invalid.
|
||||
* It's better to set current time */
|
||||
memcpy(&npp.ts, nsock_gettimeofday(), sizeof(struct timeval));
|
||||
#endif
|
||||
npp.len = pkt_header->len;
|
||||
@@ -391,31 +413,34 @@ int do_actual_pcap_read(msevent *nse) {
|
||||
|
||||
nsock_log_debug_all(nse->iod->nsp, "PCAP %s READ (IOD #%li) (EID #%li) size=%i",
|
||||
__func__, nse->iod->id, nse->id, pkt_header->caplen);
|
||||
return(1);
|
||||
return 1;
|
||||
|
||||
case 0: /* timeout */
|
||||
return(0);
|
||||
|
||||
case -1: /* error */
|
||||
fatal("pcap_next_ex() fatal error while reading from pcap: %s\n", pcap_geterr(mp->pt));
|
||||
fatal("pcap_next_ex() fatal error while reading from pcap: %s\n",
|
||||
pcap_geterr(mp->pt));
|
||||
break;
|
||||
|
||||
case -2: /* no more packets in savefile (if reading from one) */
|
||||
default:
|
||||
assert(0);
|
||||
fatal("Unexpected return code from pcap_next_ex! (%d)\n", rc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nse_readpcap(nsock_event nsee, const unsigned char **l2_data, size_t *l2_len,
|
||||
const unsigned char **l3_data, size_t *l3_len, size_t *packet_len, struct timeval *ts) {
|
||||
msevent *nse = (msevent *)nsee;
|
||||
void nse_readpcap(nsock_event nsev, const unsigned char **l2_data, size_t *l2_len,
|
||||
const unsigned char **l3_data, size_t *l3_len,
|
||||
size_t *packet_len, struct timeval *ts) {
|
||||
msevent *nse = (msevent *)nsev;
|
||||
msiod *iod = nse->iod;
|
||||
mspcap *mp = (mspcap *)iod->pcap;
|
||||
nsock_pcap *n;
|
||||
size_t l2l;
|
||||
size_t l3l;
|
||||
|
||||
nsock_pcap *n = (nsock_pcap *)fs_str(&(nse->iobuf));
|
||||
n = (nsock_pcap *)fs_str(&(nse->iobuf));
|
||||
if (fs_length(&(nse->iobuf)) < sizeof(nsock_pcap)) {
|
||||
if (l2_data)
|
||||
*l2_data = NULL;
|
||||
|
||||
Reference in New Issue
Block a user