/*
# Docsis cable modem diagnostics (cmdiag)
#
# Copyright (C) 2006-2007 Emil Penchev
#
# This program is free software, distributed under the terms of
# the GNU General Public License 2.0
#
*/


#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#include "cmodem.h"
#include "globals.h"

char retdata[500];
char oid_sid_data[100];
char cable_parameter[10];
char channel_par[20];


int hex_char_to_value( char val )
{
	if ( val >= '0' && val <= '9' ) { return val - '0'; }
	if ( val >= 'a' && val <= 'f' ) { return (val - 'a') + 10; }
	if ( val >= 'A' && val <= 'F' ) { return (val - 'A') + 10; }
 
	return -1;
}

int hex_to_int( const char* buffer )
{
	int value = 0;
	int curval;
	int curpos = 0;
	
	while ( (curval = hex_char_to_value(buffer[curpos++])) != -1 )
		value = (value<<4) | curval;
	
	return value;
}

CModem::CModem(char * cmip, char * cmtsip, char * community_str, char * ctstring) 
{
	cm_state = 1;
	cmts_state = 1;
	ip = new char[strlen(cmip) + 1];
	strcpy(ip, cmip);
	cmts_ip = new char[strlen(cmtsip) + 1];
	strcpy(cmts_ip, cmtsip);
	community_string = new char[strlen(community_str) + 1];
	strcpy(community_string, community_str);
	cmts_community_string = new char[strlen(ctstring) + 1];
	strcpy(cmts_community_string, ctstring);
}


// Create SNMP session and assemble packet
char *  CModem::snmp_session_init(const char * docsis_oid, char * device_ip, char * community_str)
{
	struct snmp_session session;
	struct snmp_session * sess_handle = NULL;
	u_char * comm_string;
	struct snmp_pdu * pdu = NULL; 
	struct snmp_pdu * response = NULL;
	struct variable_list * vars = NULL;
	int status = 0;

	if (!docsis_oid || !device_ip || !community_str)
		return NULL;
	comm_string = (u_char *) community_str;
	oid id_oid[MAX_OID_LEN];
	size_t id_len = MAX_OID_LEN;

	init_snmp("APC Check");

	char * snmp_oid = (char *) docsis_oid;
	char * snmp_host = (char *) device_ip;

	snmp_sess_init( &session );
	session.version = SNMP_VERSION_1;
	session.community = comm_string;
	session.community_len = strlen(community_str);
	session.peername = snmp_host;
 
	sess_handle = snmp_open(&session);
	if (!sess_handle) {
		if (strcmp(device_ip, ip) == 0)
			cm_state = 0;
		else 
			cmts_state = 0;
		return NULL;
	}
	pdu = snmp_pdu_create(SNMP_MSG_GET);

	read_objid(snmp_oid, id_oid, &id_len);
	snmp_add_null_var(pdu, id_oid, id_len);
 
	status = snmp_synch_response(sess_handle, pdu, &response);
	if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
		vars = response->variables;
		memset(retdata, '\0', 500);
		snprint_value(retdata, 500, vars->name, vars->name_length, vars);
	}
	else {
		if (response)
			snmp_free_pdu(response); 
		snmp_close(sess_handle);
		if (strcmp(device_ip, ip) == 0)
			cm_state = 0;
		else
			cmts_state = 0;
		return NULL;
	}
	if (response)
		snmp_free_pdu(response); 
	snmp_close(sess_handle);
	if (strcmp(device_ip, ip) == 0)
		cm_state = 1;
		else 
			cmts_state = 1;

	return retdata;
}

// Parse return SNMP data
char * CModem::get_cable_parameter(const char * docsis_oid, char * dev_ip)
{
	char part_cable_parameter[10];
	char * snmp_retdata = NULL;
	
	if (strcmp(dev_ip, ip) == 0) {
		snmp_retdata = snmp_session_init(docsis_oid, dev_ip, community_string);
	}
	else {
		snmp_retdata = snmp_session_init(docsis_oid, dev_ip, cmts_community_string);
	}
	if (!snmp_retdata)
		return NULL;
	char * numeric_part = strstr(snmp_retdata, " ");
	if (!numeric_part)
		return NULL;
	memset(cable_parameter, '\0', 10);
	memset(part_cable_parameter, '\0', 10);
	
	// Convert the return data from string to integer
	int numpart = atoi(numeric_part);
	int npart = numpart / 10;
	sprintf(cable_parameter, "%d", npart);
	
	// get rest of the data
	npart = numpart % 10;
	if (npart < 0) 
		npart = 0;

	// assemble the two parts
	sprintf(part_cable_parameter, ".%d", npart);
	strcat(cable_parameter, part_cable_parameter);
 
	return cable_parameter;
}

char * CModem::get_cm_oidsid(char * cm_ip, char * cmts_ip, char * docsis_oid)
{
	char dec_mac[50];
	char buf[5];
	int  count = 0;
	int  octs = 0;
	char tmp_buf[10];
	char oid_dec_mac[100];
	char tmp_str_sid[100];
 
	/* get MAC from Modem */
	char * snmp_retdata = snmp_session_init(CM_MAC, cm_ip, community_string);
	//CM is offline
	if (!snmp_retdata) 
		return NULL;
	char * temp_mac = strstr(snmp_retdata, " ");
	if (!temp_mac) 
		return NULL;
	strcat(temp_mac, ":");
	memset(buf, '\0', 5);
	memset(dec_mac, '\0', 50);
	memset(oid_dec_mac, '\0', 100);
	memset(oid_sid_data, '\0', 100);
	memset(tmp_str_sid, '\0', 100);
	
	int len = strlen(temp_mac);
	for (int i = 0; i <= len; i++) {
		if (temp_mac[i] != ':') {
			buf[count] = temp_mac[i];
			count++;
		}
		else {
			count = 0;
			octs = hex_to_int(buf);
			sprintf(tmp_buf, ".%d", octs);
			strcat(dec_mac, tmp_buf);
			memset(buf, '\0', 5);
		}
	}
	strcpy(oid_dec_mac, CM_SID);
	strcat(oid_dec_mac, dec_mac);	
	// Get SiD from CMTS
	snmp_retdata = snmp_session_init(oid_dec_mac, cmts_ip, cmts_community_string);
	if (!snmp_retdata) 
		return NULL;
	char * sid = strstr(snmp_retdata, " ");
	int nsid = atoi(sid);
	sprintf(tmp_str_sid, "%d", nsid);
 
	//Convert SID to OID to make query to CMTS
	strcpy(oid_sid_data, docsis_oid);
	strcat(oid_sid_data, ".");
	strcat(oid_sid_data, tmp_str_sid);
 
	return oid_sid_data;
}

char * CModem::get_channel_parameter(const char * docsis_oid)
{
	memset(channel_par, '\0', 20);
	char * snmp_retdata = snmp_session_init(docsis_oid, ip, community_string);
	if (!snmp_retdata) 
		return NULL;
	char * paramt = strstr(snmp_retdata, " ");
	float fparamt = ((atof(paramt)) / 1000000);
	sprintf(channel_par, "%f MHz", fparamt);
	
	return channel_par;
}

// Member function to get cable modem downstream power level
string * CModem::Get_Ds_Power()
{
	char * ds_level = get_cable_parameter(CM_DS_LEVEL, ip);
	if (!ds_level)
		return NULL;
	ds_power.assign(ds_level);
	
	return &ds_power;
}

// Member function to get cable modem upstream power level
string * CModem::Get_Us_Power()
{
	char * us_level = get_cable_parameter(CM_US_LEVEL, ip);
	if (!us_level)
		return NULL;
	us_power.assign(us_level);
	
	return &us_power;
}

// Member function to get cable modem downstream snr
string * CModem::Get_Ds_Snr()
{
	char * snr_ds = get_cable_parameter(CM_DS_SNR, ip);
	if (!snr_ds)
		return NULL;
	snr_downstream.assign(snr_ds);
	
	return &snr_downstream;
}

// Member function to get cable modem upstream snr
// This is pull out from the CMTS
string * CModem::Get_Us_Snr()
{
	char * oid_sid = get_cm_oidsid(ip, cmts_ip, CM_US_SNR);
	if (!oid_sid)
		return NULL;
	
	char * snr_us = get_cable_parameter(oid_sid, cmts_ip);
	if (!snr_us)
		return NULL;
	
	snr_upstream.assign(snr_us);
	
	return &snr_upstream;
}

// Get downstream cable rf flow
string * CModem::Get_RfDs_Data()
{
	char * snmp_rdata = snmp_session_init(CM_DS_RF_BYTES, ip, community_string);
	if (!snmp_rdata) 
		return NULL;
	
	char * numeric_part = strstr(snmp_rdata, " ");
	if (!numeric_part)
		return NULL;
	
	rf_downstrem_flow.assign(numeric_part);
	
	return &rf_downstrem_flow;
}

// Get upstream cable rf flow
string * CModem::Get_RfUs_Data()
{
	char * snmp_rdata = snmp_session_init(CM_US_RF_BYTES, ip, community_string);
	if (!snmp_rdata) 
		return NULL;
	
	char * numeric_part = strstr(snmp_rdata, " ");
	if (!numeric_part) 
		return NULL;
	
	rf_upsteam_flow.assign(numeric_part);
	
	return &rf_upsteam_flow;
}

// Get downstream cable rf error bytes
string * CModem::Get_RfDs_Err()
{
	char * snmp_rdata = snmp_session_init(CM_DS_RF_ERR_BYTES, ip, community_string);
	if (!snmp_rdata) 
		return NULL;
	
	char * numeric_part = strstr(snmp_rdata, " ");
	if (!numeric_part) 
		return NULL;
	
	rf_downstrem_err.assign(numeric_part);
	
	return &rf_downstrem_err;
}

// Get upstream cable rf error bytes
string * CModem::Get_RfUs_Err()
{
	char * snmp_rdata = snmp_session_init(CM_US_RF_ERR_BYTES, ip, community_string);
	if (!snmp_rdata)
		return NULL;
	
	char * numeric_part = strstr(snmp_rdata, " ");
	if (!numeric_part)
		return NULL;
	
	rf_upstream_err.assign(numeric_part);
	
	return &rf_upstream_err;
}

// Get CMTS RX power for the cable modem
string * CModem::Get_CMTS_Rx_Power()
{
	char * oid_sid = get_cm_oidsid(ip, cmts_ip, CMTS_CM_RX_POWER);
	if (!oid_sid)
		return NULL;
	
	char * rx_power = get_cable_parameter(oid_sid, cmts_ip);
	if (!rx_power)
		return NULL;
	
	receive_power_at_cmts.assign(rx_power);
	
	return &receive_power_at_cmts;
}

// Get downstream Frequency
string * CModem::Get_Ds_Freq()
{
	char * d_freq = get_channel_parameter(CM_DS_FREQ);
	if (!d_freq)
		return NULL;
	freq_downstream.assign(d_freq);
	
	return &freq_downstream;
}

// Get upstream Frequency
string * CModem::Get_Us_Freq()
{
	char * u_freq = get_channel_parameter(CM_US_FREQ);
	if (!u_freq)
		return NULL;
	freq_upstream.assign(u_freq);
	
	return &freq_upstream;
}

// Get downstream channel width
string * CModem::Get_Ds_Width()
{
	char * d_width = get_channel_parameter(CM_DS_CHANNEL_WIDTH);
	if (!d_width)
		return NULL;
    channel_width_downstream.assign(d_width);
	
	return &channel_width_downstream;
}

// Get upstream channel width
string * CModem::Get_Us_Width()
{
	char * u_width = get_channel_parameter(CM_US_CHANNEL_WIDTH);
	if (!u_width)
		return NULL;
	channel_width_upstream.assign(u_width);
	
	return &channel_width_upstream;
}

// Get cable modem uptime
string * CModem::Get_Uptime()
{
	char * snmp_retdata = snmp_session_init(CM_UPTIME, ip, community_string);
	if (!snmp_retdata) 
		return NULL;
	
	char * up_time = strstr(snmp_retdata, ") ");
	if (up_time[0] == ')')
		up_time[0] = ' ';
	
	uptime.assign(up_time);
	
	return &uptime;
}

int CModem::CM_Online()
{
	return cm_state;
}

int CModem::CMTS_Online()
{
	return cmts_state;
}


// Get cable modem mac
string * CModem::Get_Mac()
{
	int chc = 0;
	int colon_count = 0;
	
	char * snmp_retdata = snmp_session_init(CM_MAC, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * ptmac = strstr(snmp_retdata, " ");
	char * tmac = new char[strlen(snmp_retdata)];
	if (ptmac)
		strcpy(tmac, ptmac);	
	mac.clear();
	
	while (tmac[chc] != '\0') {
		if (tmac[chc] == ':') {
			colon_count++;		
			if ( (tmac[chc-2] == ' ') || (tmac[chc-2] == ':')) {
				mac.append("0");
				mac.append(1, tmac[chc-1]);
				mac.append(":");
			}
			else {
				mac.append(1, tmac[chc-2]);
				mac.append(1, tmac[chc-1]);
				mac.append(":");
			}
			if (colon_count == 5) {
				if (tmac[chc+2] == '\0') {
					mac.append("0");
					mac.append(1, tmac[chc+1]);
				}
				else {
					mac.append(1, tmac[chc+1]);
					mac.append(1, tmac[chc+2]);
				}
			} 
		}
		chc++;
	}
			
	delete tmac;
	return &mac;
}

// Get sys description
string * CModem::Get_SysDesc()
{
	char * snmp_retdata = snmp_session_init(CM_VER, ip, community_string);
	if (!snmp_retdata) 
		return NULL;
	char * ver = strstr(snmp_retdata, ": ");
	if (ver[0] == ':')
		ver[0] = ' ';
	sys_descrpition.assign(ver);
	
	return &sys_descrpition;
}
	
//Get number of times the CM reset or initialized RF interface
string * CModem::Get_CM_Resets()
{
	char * snmp_retdata = snmp_session_init(CM_RF_RESETS, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * counter_resets = strstr(snmp_retdata, " ");
	if (!counter_resets)
		return NULL;
	resets.assign(counter_resets);
	
	return &resets;
}
	
// Get number of times the CM received invalid registration response messages
string * CModem::Get_Recv_Inv_Reg()
{
	char * snmp_retdata = snmp_session_init(CM_RECV_INVALID_REGISTRATION_RESPONSES, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * recv_reg_resp = strstr(snmp_retdata, " ");
	if (!recv_reg_resp)
		return NULL;
	received_inv_reg_resp.assign(recv_reg_resp);
	
	return &received_inv_reg_resp;
}

// Get number of times the CM received invalid ranging response messages
string * CModem::Get_Recv_Inv_Rang()
{
	char * snmp_retdata = snmp_session_init(CM_RECV_INVALID_RANGING_RESPONSES, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * recv_rang_resp = strstr(snmp_retdata, " ");
	if (!recv_rang_resp)
		return NULL;
	received_inv_rang_resp.assign(recv_rang_resp);
	
	return &received_inv_rang_resp;
}

//Get number of times the ranging process was aborted by the CMTS
string * CModem::Get_Abort_Rang()
{
	char * snmp_retdata = snmp_session_init(CM_CMTS_ABORTED_RANGINGS, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * aborted_rang = strstr(snmp_retdata, " ");
	if (!aborted_rang)
		return NULL;
	aborted_rangings.assign(aborted_rang);
	
	return &aborted_rangings;
}

// Get the number of codewords received without error
string * CModem::Get_Unerror_Words()
{
	char * snmp_retdata = snmp_session_init(SIGQ_UNERROREDS, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * sig_unerr = strstr(snmp_retdata, " ");
	if (!sig_unerr)
		return NULL;
	sig_unerroreds.assign(sig_unerr);
	
	return &sig_unerroreds;
}

// Get the number of codewords received with correctable errors
string * CModem::Get_Corerror_Words()
{
	
	char * snmp_retdata = snmp_session_init(SIGQ_CORRECTEDS, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * sig_corerr = strstr(snmp_retdata, " ");
	if (!sig_corerr)
		return NULL;
	sig_correcteds.assign(sig_corerr);
	
	return &sig_correcteds;
}

// Get the number of codewords received with uncorrectable errors
string * CModem::Get_Uncorerror_Words()
{
	char * snmp_retdata = snmp_session_init(SIGQ_UNCORRECTEDS, ip, community_string);
	if (!snmp_retdata)
		return NULL;
	char * sig_uncorerr = strstr(snmp_retdata, " ");
	if (!sig_uncorerr)
		return NULL;
	sig_uncorrecteds.assign(sig_uncorerr);
	
	return &sig_uncorrecteds;
}






























	


