/*
 *      ap-mrtg.c from Access Point SNMP Utils for Linux
 *
 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 from
 * June 1991 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#if defined (__GLIBC__)
#include <libgen.h>
#endif
#include "ap-utils.h"

#define ERR_STR_V "\n\n-\n\n"
#define ERR_STR_N "999999999\n999999999\n-\n\n"

short ap_type;
char *community = NULL;
struct in_addr ap_ip;

void usage()
{
    printf(_("\nUsage:\n"));
    printf(_("\tap-mrtg -i ip -c community -t type [-b bssid] [-n name] "
	     "[-v] [-h] [-r]\n\n"));
    printf(_("Get stats from AP and return it in MRTG parsable format\n\n"));
    printf(_("-i ip        - AP ip address\n"));
    printf(_("-c community - SNMP community string\n"));
    printf(_("-t type      - statistics type <w>ireless, <e>thernet, "
	     "associated <s>tations or <l>ink quality (last one will only "));
    printf(_("work with ATMEL410 MIB devices in AP Client mode)\n"));
    printf(_("-b bssid     - mac address of the AP from which get link quality"
	     ", only if type=l\n"));
    printf(_("-n name      - AP name - for check only\n")); 
    printf(_("-v           - report MRTG about problems connecting to AP\n"));
    printf(_("-r           - reset AP when getting LinkQuality stats\n"));
    printf(_("-h           - print this help screen\n\n"));
    printf(_("ap-mrtg %s Copyright (c) 2002-2003 Roman Festchook\n\n"),
	   VERSION);
}

int main(int argc, char **argv)
{
    extern char *optarg;
    extern int optind;
    extern int opterr;
    extern int optopt;
    int opt = 0;

    struct ap {
	char mac[6];
	unsigned char q1;
	unsigned char q2;
	unsigned char channel;
	unsigned char x2;
	unsigned char options;
	unsigned char x3[5];
	unsigned char essid[32];
    } *app = NULL;

    char sysDescr_NWN[] = {
	0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00
    };
    char sysDescr_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x01, 0x01, 0x00
    };
    char bridgeOperationalMode[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x01, 0x00
    };
    char EthRxStatistics[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x07, 0x01, 0x00
    };
    char EthTxStatistics[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x07, 0x02, 0x00
    };
    char operAccessPointName[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x0A, 0x00
    };
    char wirelessStatistics[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x03, 0x01, 0x00
    };
    char AssociatedSTAsNum[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x05, 0x01, 0x00
    };
    char wirelessKnownAPs[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x07, 0x01, 0x00
    };

    struct EthRxStatistics_s *EthRxStat = NULL;
    struct EthTxStatistics_s *EthTxStat = NULL;
    struct wirelessStatistics_s *WirelessStat = NULL;
    varbind varbinds[2];
    int i, reset_flag=0;
    char message[12], bssid_flag,  stat_type = 0, *ERR_STR =
	ERR_STR_N, *bssid = NULL, *name = NULL, *cp;

#ifdef HAVE_GETTEXT
    setlocale(LC_ALL, "");
    bindtextdomain("ap-utils", LOCALEDIR);
    textdomain("ap-utils");
#endif

    if (argc < 4) {
	usage();
	exit(0);
    }

    do {
	opterr = 0;
	switch (opt = getopt(argc, argv, "i:c:t:b:n:rv")) {
	case 'i':
	    for (cp = optarg, i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
	    if (i < 3 || inet_aton(optarg, &ap_ip) == 0) {
		printf(_("Error: invalid IP-address.\n"));
		return 1;
	    }
	    break;
	case 't':
	    stat_type = optarg[0];
	    break;
	case 'v':
	    ERR_STR = ERR_STR_V;
	    break;
	case 'c':
	    community = malloc(strlen(optarg) + 1);
	    strncpy(community, optarg, strlen(optarg) + 1);
	    break;
	case 'b':
	    bssid = malloc(strlen(optarg) + 1);
	    strncpy(bssid, optarg, strlen(optarg) + 1);
	    break;
	case 'n':
	    name = malloc(strlen(optarg) + 1);
	    strncpy(name, optarg, strlen(optarg) + 1);
	    break;
	case 'r':
	    reset_flag=1;
	    break;
	case -1:
            break;
	default:
	    usage();
	    goto quit;
	}
    } while (opt != -1);

    if (!community) {
	usage();
	goto quit;
    }

    /*
     * Part detecting ap_type (ATMEL AP MIB type) follows.
     * We could use get_mib_details() here with advantage, but it would
     * have to involve 1. putting it to separate file in lib/ and
     * 2. patch it so it would not contain curses-related commands (TODO)
     */

    /* determine private MIB type according to enterprises ID */
    varbinds[0].oid = sysDescr_NWN;
    varbinds[0].len_oid = sizeof(sysDescr_NWN);
    varbinds[0].value = NULL;
    varbinds[0].len_val = 0;
    varbinds[0].type = NULL_VALUE;
    if (snmp(varbinds, 1, GET) > 0) {
        ap_type = NWN;
    } else {
	varbinds[0].oid = sysDescr_ATMEL;
	varbinds[0].len_oid = sizeof(sysDescr_ATMEL);
	varbinds[0].value = NULL;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	if (snmp(varbinds, 1, GET) > 0) {
	    ap_type = ATMEL410;
	} else {
	    sysDescr_ATMEL[5] = 0xE0;
	    sysDescr_ATMEL[6] = 0x3E;
	    varbinds[0].oid = sysDescr_ATMEL;
	    varbinds[0].len_oid = sizeof(sysDescr_ATMEL);
	    varbinds[0].value = NULL;
	    varbinds[0].len_val = 0;
	    varbinds[0].type = NULL_VALUE;
	    if (snmp(varbinds, 1, GET) > 0) {
		ap_type = ATMEL12350;
	    } else {
		printf(_("Unable to determine AP MIB type "
		    "(no response from AP)."));
		return 1;
	    }
	}
    }

    if (ap_type == NWN) {
	printf("NWN devices are not yet supported.");
	return 1;
    }

    if (ap_type == ATMEL12350) {
	bridgeOperationalMode[5] = 0xE0;
	bridgeOperationalMode[6] = 0x3E;
	EthRxStatistics[5] = 0xE0;
	EthRxStatistics[6] = 0x3E;
	EthTxStatistics[5] = 0xE0;
	EthTxStatistics[6] = 0x3E;
	operAccessPointName[5] = 0xE0;
	operAccessPointName[6] = 0x3E;
	wirelessStatistics[5] = 0xE0;
	wirelessStatistics[6] = 0x3E;
	AssociatedSTAsNum[5] = 0xE0;
	AssociatedSTAsNum[6] = 0x3E;
	wirelessKnownAPs[5] = 0xE0;
	wirelessKnownAPs[6] = 0x3E;
    }

    switch (stat_type) {

    case 'e':
	varbinds[0].oid = EthRxStatistics;
	varbinds[0].len_oid = sizeof(EthRxStatistics);
	varbinds[0].value = EthRxStatistics;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	varbinds[1].oid = EthTxStatistics;
	varbinds[1].len_oid = sizeof(EthTxStatistics);
	varbinds[1].value = EthTxStatistics;
	varbinds[1].len_val = 0;
	varbinds[1].type = NULL_VALUE;

	if (snmp(varbinds, 2, GET) < 2) {
	    printf(ERR_STR);
	    return 1;
	}

	if (varbinds[0].len_val == 64) {
	    if (EthRxStat)
		free(EthRxStat);
	    EthRxStat =
		(struct EthRxStatistics_s *) malloc(varbinds[0].
						       len_val);
	    memcpy(EthRxStat, varbinds[0].value, varbinds[0].len_val);
	} else {
	    printf(ERR_STR);
	    return 1;
	}

	if (varbinds[1].len_val == 56) {
	    if (EthTxStat)
		free(EthTxStat);
	    EthTxStat =
		(struct EthTxStatistics_s *) malloc(varbinds[1].
						       len_val);
	    memcpy(EthTxStat, varbinds[1].value, varbinds[1].len_val);
	} else {
	    printf(ERR_STR);
	    return 1;
	}
	printf("%u\n%u\n", swap4(EthRxStat->TotalBytesRx),
	       swap4(EthTxStat->TotalBytesTx));
	if (EthRxStat)
	    free(EthRxStat);
	if (EthTxStat)
	    free(EthTxStat);
	break;
    case 'w':
	varbinds[0].oid = wirelessStatistics;
	varbinds[0].len_oid = sizeof(wirelessStatistics);
	varbinds[0].value = wirelessStatistics;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;

	if (snmp(varbinds, 1, GET) <= 0) {
	    printf(ERR_STR);
	    return 1;
	}

	if (varbinds[0].len_val == 88 || varbinds[0].len_val == 104) {
	    /*
	     * 88 ... using traditional ATMEL 12350 MIB
	     * 104 .. using functionally enhanced ATMEL 12350 MIB by EZYNET
	     */
	    if (WirelessStat)
		free(WirelessStat);
	    WirelessStat =
		(struct wirelessStatistics_s *) malloc(varbinds[0].len_val);
	    memcpy(WirelessStat, varbinds[0].value, varbinds[0].len_val);
	} else {
	    printf(ERR_STR);
	    return 1;
	}
	printf("%u\n%u\n",
	       swap4(WirelessStat->UnicastReceivedPackets) +
	       swap4(WirelessStat->BroadcastReceivedPackets) +
	       swap4(WirelessStat->MulticastReceivedPackets),
	       swap4(WirelessStat->UnicastTransmittedPackets) +
	       swap4(WirelessStat->BroadcastTransmittedPackets) +
	       swap4(WirelessStat->MulticastTransmittedPackets));
	break;

    case 's':
	varbinds[0].oid = AssociatedSTAsNum;
	varbinds[0].len_oid = sizeof(AssociatedSTAsNum);
	varbinds[0].value = AssociatedSTAsNum;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;

	if (snmp(varbinds, 1, GET) <= 0) {
	    printf(ERR_STR);
	    return 1;
	}

	printf("%u\n0\n", *varbinds[0].value);
	break;

    case 'l':
	varbinds[0].oid = bridgeOperationalMode;
	varbinds[0].len_oid = sizeof(bridgeOperationalMode);
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;

	if (snmp(varbinds, 1, GET) <= 0) {
	    printf(ERR_STR);
	    return 1;
	}

	if (!(ap_type == ATMEL410 && *(varbinds[0].value) == 3)) {
	    printf(ERR_STR);
	    return 1;
	}

	if (reset_flag) {
		if (SysReset()) {
		    printf(ERR_STR);
		    return 1;
		}
	        sleep(10);
	}

	varbinds[0].oid = wirelessKnownAPs;
	varbinds[0].len_oid = sizeof(wirelessKnownAPs);
	varbinds[0].type = NULL_VALUE;
	varbinds[0].len_val = 0;

	if (snmp(varbinds, 1, GET) <= 0) {
	    printf(ERR_STR);
	    return 1;
	}
	bssid_flag = 1;
	for (i = 0; i < varbinds[0].len_val; i += 48) {
	    if (app)
		free(app);
	    app = (struct ap *) malloc(48);
	    memcpy(app, varbinds[0].value + i, 48);
	    if (!app->channel)
		continue;
	    if (bssid) {
		sprintf(message, "%02X%02X%02X%02X%02X%02X",
			app->mac[0] & 0xFF, app->mac[1] & 0xFF,
			app->mac[2] & 0xFF, app->mac[3] & 0xFF,
			app->mac[4] & 0xFF, app->mac[5] & 0xFF);
		if (memcmp(message, bssid, 12))
		    continue;
	    };
	    printf("%d\n%d\n", app->q2,
		   96 - app->q1);
	    bssid_flag = 0;
	    break;
	}
	if (bssid_flag)
	    printf(ERR_STR);
	break;
    default:
	usage();
	goto quit;
    }

    printf("-\n");

    if ( name != NULL ) {
        varbinds[0].oid = operAccessPointName;
        varbinds[0].len_oid = sizeof(operAccessPointName);
        varbinds[0].len_val = 0;
        varbinds[0].type = NULL_VALUE;
        if (snmp(varbinds, 1, GET) <= 0) {
        	printf("\n");
		return 1;
        }

        for (i = 0; i < 32 && *(varbinds[0].value + i); i++)
		putchar(*(varbinds[0].value + i));
	        putchar('\n');
          if (strncmp(name,varbinds[0].value,strlen(name)) ){
		return 2;
        }
    } else {
        printf("-\n");
    }

  quit:
    if (community)
	free(community);
    if (app)
	free(app);
    if (bssid)
	free(bssid);
    if (name)
	free(name);
    return 0;
}
