/*
 *      ap-rrd.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 _("Error getting data from AP %s\n")

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

void usage()
{
    printf(_("\nUsage:\n"));
    printf(_
          ("\tap-rrd -i ip -c community -t type -d db_file [-b bssid] [-n name] [-a aptype] [-h] [-r] \n\n"));
    printf(_
	   ("Get stats from AP and put it in to specified RRDtool database\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 in client mode\n"));
    printf(_("-d db_file   - RRD database file with full path\n"));
    printf(_
	   ("-b bssid     - mac address of the AP to which get link quality, only if type=l\n"));
    printf(_("-n name     - AP name - for check only\n")); 
    printf(_("-a aptype    - AP type - 410 (default) or 510 for ATMEL12350's, like the ME-102\n"));
    printf(_("-r           - reset AP when getting LinkQuality stats\n"));
    printf(_("-h           - print this help screen\n\n"));
    printf(_("ap-rrd %s Copyright (c) 2002-2004 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 Wireless[] =
	{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x03, 0x01,
	0x00
    };
    char EthRx[] =
	{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x07, 0x01,
	0x00
    };
    char EthTx[] =
	{ 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 StasNum[] =
	{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x05, 0x01,
	0x00
    };
    char KnownAP[] =
	{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x07,
	0x01, 0x00
    };
    char bridgeOperationalMode[] =
	{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01,
	0x01, 0x04, 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, *bssid = NULL, *name = NULL, *rrd_file = NULL, cmd[1024];

#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:a:d:r")) {
	case 'i':
	    if (inet_aton(optarg, &ap_ip) == 0) {
		printf(_("Invalid IP-address\n"));
		return 1;
	    }
	    break;
	case 't':
	    stat_type = optarg[0];
	    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 'd':
	    rrd_file = malloc(strlen(optarg) + 1);
	    strncpy(rrd_file, 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 'a':
            if ( strcmp( optarg, "510\0" ) == 0) {
              Wireless[5] = 0xE0;
              Wireless[6] = 0x3E;
              EthRx[5] = 0xE0;
              EthRx[6] = 0x3E;
              EthTx[5] = 0xE0;
              EthTx[6] = 0x3E;
              operAccessPointName[5] = 0xE0;
              operAccessPointName[6] = 0x3E;
              StasNum[5] = 0xE0;
              StasNum[6] = 0x3E;
              KnownAP[5] = 0xE0;
              KnownAP[6] = 0x3E;
              bridgeOperationalMode[5] = 0xE0;
              bridgeOperationalMode[6] = 0x3E;
            } else if (strcmp(optarg, "410") == 0) {
              /* nothing - hard-coded defaults are fine */
            } else {
              /* Invalid AP-Type */
              printf(_("Invalid AP-Type '%s' - valid types are 510 or 410\n"), optarg);
              return 1;
            }
            break;
	case -1:
            break;
	default:
	    usage();
	    goto quit;
	}
    } while (opt != -1);

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

    switch (stat_type) {

    case 'e':

	varbinds[0].oid = EthRx;
	varbinds[0].len_oid = sizeof(EthRx);
	varbinds[0].value = EthRx;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	varbinds[1].oid = EthTx;
	varbinds[1].len_oid = sizeof(EthTx);
	varbinds[1].value = EthTx;
	varbinds[1].len_val = 0;
	varbinds[1].type = NULL_VALUE;

	if (snmp(varbinds, 2, GET) <= 0) {
	    printf(ERR_STR, inet_ntoa(ap_ip));
	    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, inet_ntoa(ap_ip));
	    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, inet_ntoa(ap_ip));
	    return 1;
	}
	sprintf(cmd, "rrdtool update %s N:%u:%u", rrd_file, swap4(EthRxStat->TotalBytesRx), swap4(EthTxStat->TotalBytesTx));
	system(cmd);
	if (EthRxStat)
	    free(EthRxStat);
	if (EthTxStat)
	    free(EthTxStat);
	break;
    case 'w':
	varbinds[0].oid = Wireless;
	varbinds[0].len_oid = sizeof(Wireless);
	varbinds[0].value = Wireless;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;

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

	if (varbinds[0].len_val == 88) {
	    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, inet_ntoa(ap_ip));
	    return 1;
	}
	sprintf(cmd, "rrdtool update %s N:%u:%u", rrd_file,
	       swap4(WirelessStat->UnicastReceivedPackets) +
	       swap4(WirelessStat->BroadcastReceivedPackets) +
	       swap4(WirelessStat->MulticastReceivedPackets),
	       swap4(WirelessStat->UnicastTransmittedPackets) +
	       swap4(WirelessStat->BroadcastTransmittedPackets) +
	       swap4(WirelessStat->MulticastTransmittedPackets));
	system(cmd);
	break;

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

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

	sprintf(cmd, "rrdtool update %s N:%u", rrd_file, *varbinds[0].value);
	system(cmd);
	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, inet_ntoa(ap_ip));
	    return 1;
	}

	if (*(varbinds[0].value) != 3) {
	    printf(ERR_STR, inet_ntoa(ap_ip));
	    return 1;
	}

	if (reset_flag) {
		if (SysReset()) {
		    printf(ERR_STR, inet_ntoa(ap_ip));
		    return 1;
		}
	        sleep(10);
	}
		
	varbinds[0].oid = KnownAP;
	varbinds[0].len_oid = sizeof(KnownAP);
	varbinds[0].type = NULL_VALUE;
	varbinds[0].len_val = 0;

	if (snmp(varbinds, 1, GET) <= 0) {
	    printf(ERR_STR, inet_ntoa(ap_ip));
	    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;
	    };
	sprintf(cmd, "rrdtool update %s N:%u:%u", rrd_file, app->q2, 96 - app->q1);
	system(cmd);
	    bssid_flag = 0;
	    break;
	}
	if (bssid_flag)
	    printf(ERR_STR, inet_ntoa(ap_ip));
	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;
}
