Blame apps/snmppcap.c

Packit fcad23
/*
Packit fcad23
 * Copyright (c) 2015, Arista Networks, inc.
Packit fcad23
 * All rights reserved.
Packit fcad23
 *
Packit fcad23
 * Redistribution and use in source and binary forms, with or without
Packit fcad23
 * modification, are permitted provided that the following conditions
Packit fcad23
 * are met:
Packit fcad23
 *
Packit fcad23
 * 1. Redistributions of source code must retain the above copyright
Packit fcad23
 * notice, this list of conditions and the following disclaimer.
Packit fcad23
 *
Packit fcad23
 * 2. Redistributions in binary form must reproduce the above copyright
Packit fcad23
 * notice, this list of conditions and the following disclaimer in the
Packit fcad23
 * documentation and/or other materials provided with the distribution.
Packit fcad23
 *
Packit fcad23
 * 3. Neither the name of the copyright holder nor the names of its
Packit fcad23
 * contributors may be used to endorse or promote products derived from
Packit fcad23
 * this software without specific prior written permission.
Packit fcad23
 *
Packit fcad23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit fcad23
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit fcad23
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Packit fcad23
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
Packit fcad23
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Packit fcad23
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
Packit fcad23
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit fcad23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit fcad23
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit fcad23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit fcad23
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit fcad23
 */
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
#include <net-snmp/net-snmp-includes.h>
Packit fcad23
#include <net-snmp/library/large_fd_set.h>
Packit fcad23
#include <stdio.h>
Packit fcad23
#include <unistd.h>
Packit fcad23
#include <sys/types.h>
Packit fcad23
#include <sys/stat.h>
Packit fcad23
#include <fcntl.h>
Packit fcad23
#include <pcap/pcap.h>
Packit fcad23
Packit fcad23
#define FAKE_FD 3
Packit fcad23
/*
Packit fcad23
 * This is a funny little program, hooking together callbacks
Packit fcad23
 * to get packets from a pcap format file to go through
Packit fcad23
 * net-snmp's main loop.  Both net-snmp and libpcap have
Packit fcad23
 * callback-based processing, so:
Packit fcad23
 *
Packit fcad23
 * We create a fake snmp transport that calls snmppcap_recv()
Packit fcad23
 * to receive a packet, and use snmp_add() to add the session
Packit fcad23
 * and the transport directly.
Packit fcad23
 *
Packit fcad23
 * We create a session callback on the session that just
Packit fcad23
 * prints out the varbind list that we got.
Packit fcad23
 *
Packit fcad23
 * We use the libpcap pcap_dispatch() to loop through all
Packit fcad23
 * the packets in the pcap file, and then pretend to snmp_read2()
Packit fcad23
 * that a fake file descriptor is ready.  Luckily, there is
Packit fcad23
 * only one session active, and it happens to also claim to
Packit fcad23
 * be using that file descriptor, so snmplib calls the transport
Packit fcad23
 * receive function, snmppcap_recv().  If the packet parses,
Packit fcad23
 * we get a callback at snmppcap_callback().  If it doesn't,
Packit fcad23
 * you may need to use the -D options.
Packit fcad23
 */
Packit fcad23
typedef struct mystuff {
Packit fcad23
    int pktnum;
Packit fcad23
} mystuff_t;
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * These two globals are used to communicate between handle_pcap()
Packit fcad23
 * and snmppcap_recv().  Don't try to multi-thread!  :-)
Packit fcad23
 */
Packit fcad23
const void *recv_data;
Packit fcad23
int recv_datalen;
Packit fcad23
Packit fcad23
int
Packit fcad23
snmppcap_recv(netsnmp_transport *t, void *buf, int bufsiz, void **opaque, int *opaque_len)
Packit fcad23
{
Packit fcad23
    if (bufsiz > recv_datalen) {
Packit fcad23
        memcpy(buf, recv_data, recv_datalen);
Packit fcad23
        return recv_datalen;
Packit fcad23
    } else {
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * snmplib calls us back with the received packet.
Packit fcad23
 */
Packit fcad23
static int
Packit fcad23
snmppcap_callback(int op, netsnmp_session *sess, int reqid, netsnmp_pdu *pdu,
Packit fcad23
                  void *magic)
Packit fcad23
{
Packit fcad23
    mystuff_t *mystuff = (mystuff_t *)magic;
Packit fcad23
    netsnmp_variable_list *vars;
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * We ignore op, since we know there is only way we can be
Packit fcad23
     * called back, since this is not a "real" transport.
Packit fcad23
     */
Packit fcad23
    printf( "Packet %d PDU contents:\n", mystuff->pktnum );
Packit fcad23
    /*
Packit fcad23
     * TODO: print PDU type and other info?
Packit fcad23
     */
Packit fcad23
    for (vars = pdu->variables; vars; vars = vars->next_variable) {
Packit fcad23
       printf( "   " );
Packit fcad23
       print_variable(vars->name, vars->name_length, vars );
Packit fcad23
    }
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
handle_pcap(u_char *user, const struct pcap_pkthdr *h,
Packit fcad23
                                         const u_char *bytes)
Packit fcad23
{
Packit fcad23
    size_t len;
Packit fcad23
    const u_char *buf;
Packit fcad23
    int skip;
Packit fcad23
    mystuff_t *mystuff = (mystuff_t *)user;
Packit fcad23
    netsnmp_large_fd_set lfdset;
Packit fcad23
Packit fcad23
    mystuff->pktnum++;
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * If it's not a full packet, then we can't parse it.
Packit fcad23
     */
Packit fcad23
    if ( h->caplen < h->len ) {
Packit fcad23
        printf( "Skipping packet #%d; we only have %d of %d bytes\n", mystuff->pktnum, h->caplen, h->len );
Packit fcad23
        return;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * For now, no error checking and almost no parsing.
Packit fcad23
     * Assume that we have all Ethernet/IPv4/UDP/SNMP.
Packit fcad23
     */
Packit fcad23
    skip = 14 /* Ethernet */ + 20 /* IPv4 */ + 8 /* UDP */;
Packit fcad23
    buf = bytes + skip;
Packit fcad23
    len = h->len - skip;
Packit fcad23
Packit fcad23
    printf( "Packet #%d:\n", mystuff->pktnum );
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * Store the data in the globals that we use to communicate
Packit fcad23
     */
Packit fcad23
    recv_data = buf;
Packit fcad23
    recv_datalen = len;
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * We call snmp_read2() pretending that our
Packit fcad23
     * fake file descriptor is ready to read.
Packit fcad23
     * This is a funny API to fake up - we need to
Packit fcad23
     * set our fake file descriptor so that our fake
Packit fcad23
     * receive function gets called.
Packit fcad23
     */
Packit fcad23
    netsnmp_large_fd_set_init(&lfdset, FD_SETSIZE);
Packit fcad23
    netsnmp_large_fd_setfd(FAKE_FD, &lfdset);
Packit fcad23
    snmp_read2(&lfdset);
Packit fcad23
    netsnmp_large_fd_set_cleanup(&lfdset);
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
usage(void)
Packit fcad23
{
Packit fcad23
    fprintf(stderr, "USAGE: snmppcap [OPTIONS] FILE\n\n");
Packit fcad23
    /* can't use snmp_parse_args_usage because it assumes an agent */
Packit fcad23
    snmp_parse_args_descriptions(stderr);
Packit fcad23
}
Packit fcad23
Packit fcad23
int main(int argc, char **argv)
Packit fcad23
{
Packit fcad23
    netsnmp_session *ss;
Packit fcad23
    netsnmp_transport *transport;
Packit fcad23
    int arg;
Packit fcad23
    char errbuf[PCAP_ERRBUF_SIZE];
Packit fcad23
    char *fname;
Packit fcad23
    pcap_t *p;
Packit fcad23
    mystuff_t mystuff;
Packit fcad23
Packit fcad23
    ss = SNMP_MALLOC_TYPEDEF(netsnmp_session);
Packit fcad23
    /*
Packit fcad23
     * snmp_parse_args usage here is totally overkill, but trying to
Packit fcad23
     * parse -D
Packit fcad23
     */
Packit fcad23
    switch (arg = snmp_parse_args(argc, argv, ss, "", NULL)) {
Packit fcad23
    case NETSNMP_PARSE_ARGS_ERROR:
Packit fcad23
        exit(1);
Packit fcad23
    case NETSNMP_PARSE_ARGS_SUCCESS_EXIT:
Packit fcad23
        exit(0);
Packit fcad23
    case NETSNMP_PARSE_ARGS_ERROR_USAGE:
Packit fcad23
        usage();
Packit fcad23
        exit(1);
Packit fcad23
    default:
Packit fcad23
        break;
Packit fcad23
    }
Packit fcad23
    if (arg != argc) {
Packit fcad23
        fprintf(stderr, "Specify exactly one file name\n");
Packit fcad23
        usage();
Packit fcad23
        exit(1);
Packit fcad23
    }
Packit fcad23
    fname = argv[ arg-1 ];
Packit fcad23
    p = pcap_open_offline( fname, errbuf );
Packit fcad23
    if ( p == NULL ) {
Packit fcad23
        fprintf(stderr, "%s: %s\n", fname, errbuf );
Packit fcad23
        return 1;
Packit fcad23
    }
Packit fcad23
    if ( pcap_datalink( p ) != DLT_EN10MB) {
Packit fcad23
        fprintf(stderr, "Only Ethernet pcaps currently supported\n");
Packit fcad23
        return 2;
Packit fcad23
    }
Packit fcad23
    transport = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
Packit fcad23
    if ( transport == NULL ) {
Packit fcad23
        fprintf(stderr, "Could not malloc transport\n" );
Packit fcad23
        return 3;
Packit fcad23
    }
Packit fcad23
    /*
Packit fcad23
     * We set up just enough of the transport to fake the main
Packit fcad23
     * loop into calling us back.
Packit fcad23
     */
Packit fcad23
    transport->sock = FAKE_FD;        /* nobody actually uses this as a file descriptor */
Packit fcad23
    transport->f_recv = snmppcap_recv;
Packit fcad23
Packit fcad23
    ss->callback = snmppcap_callback;
Packit fcad23
    ss->callback_magic = (void *)&mystuff;
Packit fcad23
Packit fcad23
    /* todo: add the option of a filter here */
Packit fcad23
    mystuff.pktnum = 0;
Packit fcad23
    /* todo: user, etc. parsing. */
Packit fcad23
    ss->securityModel = SNMP_SEC_MODEL_USM;
Packit fcad23
    printf("flags %lx securityModel %d version %ld securityNameLen %" NETSNMP_PRIz "d securityEngineIDLen %" NETSNMP_PRIz "d\n",
Packit fcad23
          ss->flags, ss->securityModel, ss->version,
Packit fcad23
          ss->securityNameLen, ss->securityEngineIDLen);
Packit fcad23
    create_user_from_session(ss);
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * We use snmp_add() to specify the transport
Packit fcad23
     * explicitly.
Packit fcad23
     */
Packit fcad23
    snmp_add(ss, transport, NULL, NULL);
Packit fcad23
Packit fcad23
    pcap_loop(p, -1, handle_pcap, (void *)&mystuff);
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}