2729 lines
59 KiB
C
2729 lines
59 KiB
C
/*
|
|
*
|
|
* ===================================
|
|
* HARP | Host ATM Research Platform
|
|
* ===================================
|
|
*
|
|
*
|
|
* This Host ATM Research Platform ("HARP") file (the "Software") is
|
|
* made available by Network Computing Services, Inc. ("NetworkCS")
|
|
* "AS IS". NetworkCS does not provide maintenance, improvements or
|
|
* support of any kind.
|
|
*
|
|
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
|
|
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
|
|
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
|
|
* In no event shall NetworkCS be responsible for any damages, including
|
|
* but not limited to consequential damages, arising from or relating to
|
|
* any use of the Software or related support.
|
|
*
|
|
* Copyright 1994-1998 Network Computing Services, Inc.
|
|
*
|
|
* Copies of this Software may be made, however, the above copyright
|
|
* notice must be reproduced on all copies.
|
|
*
|
|
* @(#) $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* User utilities
|
|
* --------------
|
|
*
|
|
* Implement very minimal ILMI address registration.
|
|
*
|
|
* Implement very crude and basic support for "cracking" and
|
|
* "encoding" SNMP PDU's to support ILMI prefix and NSAP address
|
|
* registration. Code is not robust nor is it meant to provide any
|
|
* "real" SNMP support. Much of the code expects predetermined values
|
|
* and will fail if anything else is found. Much of the "encoding" is
|
|
* done with pre-computed PDU's.
|
|
*
|
|
* See "The Simple Book", Marshall T. Rose, particularly chapter 5,
|
|
* for ASN and BER information.
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netatm/port.h>
|
|
#include <netatm/atm.h>
|
|
#include <netatm/atm_if.h>
|
|
#include <netatm/atm_sigmgr.h>
|
|
#include <netatm/atm_sap.h>
|
|
#include <netatm/atm_sys.h>
|
|
#include <netatm/atm_ioctl.h>
|
|
#include <dev/hfa/fore_aali.h>
|
|
#include <dev/hfa/fore_slave.h>
|
|
#include <dev/hfa/fore_stats.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <libatm.h>
|
|
#include <libgen.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
#ifndef lint
|
|
__RCSID("@(#) $FreeBSD$");
|
|
#endif
|
|
|
|
|
|
#define MAX_LEN 9180
|
|
|
|
#define MAX_UNITS 8
|
|
|
|
/*
|
|
* Define some ASN types
|
|
*/
|
|
#define ASN_INTEGER 0x02
|
|
#define ASN_OCTET 0x04
|
|
#define ASN_NULL 0x05
|
|
#define ASN_OBJID 0x06
|
|
#define ASN_SEQUENCE 0x30
|
|
#define ASN_IPADDR 0x40
|
|
#define ASN_TIMESTAMP 0x43
|
|
|
|
static const char *Var_Types[] = {
|
|
"",
|
|
"",
|
|
"ASN_INTEGER",
|
|
"",
|
|
"ASN_OCTET",
|
|
"ASN_NULL",
|
|
"ASN_OBJID"
|
|
};
|
|
|
|
/*
|
|
* Define SNMP PDU types
|
|
*/
|
|
#define PDU_TYPE_GET 0xA0
|
|
#define PDU_TYPE_GETNEXT 0xA1
|
|
#define PDU_TYPE_GETRESP 0xA2
|
|
#define PDU_TYPE_SET 0xA3
|
|
#define PDU_TYPE_TRAP 0xA4
|
|
|
|
static const char *const PDU_Types[] = {
|
|
"GET REQUEST",
|
|
"GETNEXT REQUEST",
|
|
"GET RESPONSE",
|
|
"SET REQUEST",
|
|
"TRAP",
|
|
" ?? ",
|
|
" ??? "
|
|
};
|
|
|
|
/*
|
|
* Define TRAP codes
|
|
*/
|
|
#define TRAP_COLDSTART 0
|
|
#define TRAP_WARMSTART 1
|
|
#define TRAP_LINKDOWN 2
|
|
#define TRAP_LINKUP 3
|
|
#define TRAP_AUTHFAIL 4
|
|
#define TRAP_EGPLOSS 5
|
|
#define TRAP_ENTERPRISE 6
|
|
|
|
/*
|
|
* Define SNMP Version numbers
|
|
*/
|
|
#define SNMP_VERSION_1 1
|
|
#define SNMP_VERSION_2 2
|
|
|
|
/*
|
|
* SNMP Error-status values
|
|
*/
|
|
#define SNMP_ERR_NOERROR 0
|
|
#define SNMP_ERR_TOOBIG 1
|
|
#define SNMP_ERR_NOSUCHNAME 2
|
|
#define SNMP_ERR_BADVALUE 3
|
|
#define SNMP_ERR_READONLY 4
|
|
#define SNMP_ERR_GENERR 5
|
|
|
|
/*
|
|
* Max string length for Variable
|
|
*/
|
|
#define STRLEN 128
|
|
|
|
/*
|
|
* Unknown variable
|
|
*/
|
|
#define VAR_UNKNOWN -1
|
|
|
|
/*
|
|
* Define our internal representation of an OBJECT IDENTIFIER
|
|
*/
|
|
struct objid {
|
|
int oid[128];
|
|
};
|
|
typedef struct objid Objid;
|
|
|
|
/*
|
|
* Define a Veriable classso that we can handle multiple GET/SET's
|
|
* per PDU.
|
|
*/
|
|
typedef struct variable Variable;
|
|
struct variable {
|
|
Objid oid;
|
|
int type;
|
|
union {
|
|
int ival; /* INTEGER/TIMESTAMP */
|
|
Objid oval; /* OBJID */
|
|
long aval; /* IPADDR */
|
|
char sval[STRLEN]; /* OCTET */
|
|
} var;
|
|
Variable *next;
|
|
};
|
|
|
|
/*
|
|
* Every SNMP PDU has the first four fields of this header. The only type
|
|
* which doesn't have the last three fields is the TRAP type.
|
|
*/
|
|
struct snmp_header {
|
|
int pdulen;
|
|
int version;
|
|
char community[64];
|
|
int pdutype;
|
|
|
|
/* GET/GETNEXT/GETRESP/SET */
|
|
int reqid;
|
|
int error;
|
|
int erridx;
|
|
|
|
/* TRAP */
|
|
Objid enterprise;
|
|
int ipaddr;
|
|
int generic_trap;
|
|
int specific_trap;
|
|
|
|
int varlen;
|
|
Variable *head,
|
|
*tail;
|
|
};
|
|
typedef struct snmp_header Snmp_Header;
|
|
|
|
Snmp_Header *ColdStart_Header;
|
|
Snmp_Header *PDU_Header;
|
|
|
|
/*
|
|
* Define some OBJET IDENTIFIERS that we'll try to reply to:
|
|
*
|
|
* sysUpTime: number of time ticks since this deamon came up
|
|
* netpfx_oid: network prefix table
|
|
* unitype: is this a PRIVATE or PUBLIC network link
|
|
* univer: which version of UNI are we running
|
|
* devtype: is this a USER or NODE ATM device
|
|
* setprefix: used when the switch wants to tell us its NSAP prefix
|
|
* foresiggrp: FORE specific Objid we see alot of (being connected to FORE
|
|
* switches...)
|
|
*/
|
|
Objid Objids[] = {
|
|
#define SYS_OBJID 0
|
|
{{ 8, 43, 6, 1, 2, 1, 1, 2, 0 }},
|
|
#define UPTIME_OBJID 1
|
|
{{ 8, 43, 6, 1, 2, 1, 1, 3, 0 }},
|
|
#define PORT_OBJID 2
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 1, 1, 1, 1, 0 }},
|
|
#define IPNM_OBJID 3
|
|
{{ 10, 43, 6, 1, 4, 1, 353, 2, 1, 2, 0 }},
|
|
#define LAYER_OBJID 4
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 1, 0 }},
|
|
#define MAXVCC_OBJID 5
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 3, 0 }},
|
|
#define UNITYPE_OBJID 6
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 8, 0 }},
|
|
#define UNIVER_OBJID 7
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 9, 0 }},
|
|
#define DEVTYPE_OBJID 8
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 2, 1, 1, 10, 0 }},
|
|
#define ADDRESS_OBJID 9
|
|
{{ 8, 43, 6, 1, 4, 1, 353, 2, 6 }},
|
|
#define NETPFX_OBJID 10
|
|
{{ 9, 43, 6, 1, 4, 1, 353, 2, 7, 1 }},
|
|
#define MY_OBJID 11
|
|
{{ 7, 43, 6, 1, 4, 1, 9999, 1 }},
|
|
#define SETPFX_OBJID 12 /* ATM Forum says 1=valid, 2=invalid, not 0! */
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 7, 1, 1, 3, 0 }},
|
|
#define ENTERPRISE_OBJID 13
|
|
{{ 8, 43, 6, 1, 4, 1, 3, 1, 1 }},
|
|
#define ATMF_PORTID 14
|
|
{{ 10, 43, 6, 1, 4, 1, 353, 2, 1, 4, 0 }},
|
|
#define ATMF_SYSID 15
|
|
{{ 12, 43, 6, 1, 4, 1, 353, 2, 1, 1, 1, 8, 0 }},
|
|
#define MADGE_OBJECT1 16 /* I don't have a clue ... -RH */
|
|
{{ 9, 43, 6, 1, 4, 1, 353, 2, 7, 99 }},
|
|
};
|
|
|
|
#define NUM_OIDS (sizeof(Objids)/sizeof(Objid))
|
|
|
|
#define UNIVER_UNI20 1
|
|
#define UNIVER_UNI30 2
|
|
#define UNIVER_UNI31 3
|
|
#define UNIVER_UNI40 4
|
|
#define UNIVER_UNKNOWN 5
|
|
|
|
#define UNITYPE_PUBLIC 1
|
|
#define UNITYPE_PRIVATE 2
|
|
|
|
#define DEVTYPE_USER 1
|
|
#define DEVTYPE_NODE 2
|
|
|
|
/* For print_pdu() */
|
|
#define PDU_SEND 1
|
|
#define PDU_RECV 2
|
|
|
|
/*
|
|
* ILMI protocol states
|
|
*/
|
|
enum ilmi_states {
|
|
ILMI_UNKNOWN, /* Uninitialized */
|
|
ILMI_COLDSTART, /* We need to send a COLD_START trap */
|
|
ILMI_INIT, /* Ensure that switch has reset */
|
|
ILMI_REG, /* Looking for SET message */
|
|
ILMI_RUNNING /* Normal processing */
|
|
};
|
|
|
|
static const char *ILMI_State[] = {
|
|
"UNKNOWN",
|
|
"COLDSTART",
|
|
"INIT",
|
|
"REG",
|
|
"RUNNING"
|
|
};
|
|
|
|
/*
|
|
* Our (incrementing) Request ID
|
|
*/
|
|
int Req_ID;
|
|
|
|
/*
|
|
* Temporary buffer for building response packets. Should help ensure
|
|
* that we aren't accidently overwriting some other memory.
|
|
*/
|
|
u_char Resp_Buf[1024];
|
|
|
|
/*
|
|
* Copy the reponse into a buffer we can modify without
|
|
* changing the original...
|
|
*/
|
|
#define COPY_RESP(resp) \
|
|
bcopy ( (resp), Resp_Buf, (resp)[0] + 1 )
|
|
|
|
int NUnits;
|
|
|
|
/*
|
|
* fd for units which have seen a coldStart TRAP and are now exchaning SNMP requests
|
|
*/
|
|
int ilmi_fd[MAX_UNITS + 1];
|
|
/*
|
|
* enum ilmi_states for this unit
|
|
*/
|
|
int ilmi_state[MAX_UNITS + 1];
|
|
/*
|
|
* Local copy for HARP physical configuration information
|
|
*/
|
|
struct air_cfg_rsp Cfg[MAX_UNITS + 1];
|
|
/*
|
|
* Local copy for HARP interface configuration information
|
|
*/
|
|
struct air_int_rsp Intf[MAX_UNITS + 1];
|
|
|
|
/*
|
|
* addressEntry table
|
|
*/
|
|
Objid addressEntry[MAX_UNITS + 1];
|
|
|
|
/*
|
|
* When this daemon started
|
|
*/
|
|
struct timeval starttime;
|
|
|
|
int Debug_Level = 0;
|
|
int foregnd = 0; /* run in the foreground? */
|
|
|
|
char *progname;
|
|
char hostname[80];
|
|
|
|
/* File to write debug messages to */
|
|
#define LOG_FILE "/var/log/ilmid"
|
|
FILE *Log; /* File descriptor for log messages */
|
|
|
|
static const char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
|
|
|
/*
|
|
* function declarations
|
|
*/
|
|
static void write_timestamp (void);
|
|
static void hexdump (FILE *, u_int8_t *, int, char *);
|
|
static int asn_get_pdu_len (u_char **, int *);
|
|
static int asn_get_encoded (u_char **, int *);
|
|
static int asn_get_int (u_char **, int *);
|
|
static void asn_set_int (u_char **, int);
|
|
static void print_objid (Objid *);
|
|
static void asn_get_objid (u_char **, Objid *, int *);
|
|
static int asn_put_objid (u_char **, Objid *);
|
|
static void asn_get_octet (u_char **, char *, int *);
|
|
static void print_header (Snmp_Header *);
|
|
static void parse_oids (Snmp_Header *, u_char **);
|
|
static int oid_cmp (Objid *, Objid *);
|
|
static int oid_ncmp (Objid *, Objid *, int);
|
|
static int find_var (Variable *);
|
|
static int get_ticks (void);
|
|
static void build_pdu (Snmp_Header *, int);
|
|
static void free_pdu (Snmp_Header *);
|
|
static void print_pdu (int, int, Snmp_Header *, int, u_char *);
|
|
static void send_resp (int, Snmp_Header *, u_char *);
|
|
static void init_ilmi (void);
|
|
static void ilmi_open (void);
|
|
static void get_local_ip (int, long *);
|
|
static void set_prefix (Objid *, Snmp_Header *, int);
|
|
static void set_address (Snmp_Header *, int);
|
|
static void process_get (Snmp_Header *, int);
|
|
static int lmi_object_find (Variable *);
|
|
static int lmi_rcvcmd_getnext(Snmp_Header *, int);
|
|
static int lmi_rcvcmd_trap (Snmp_Header *, int);
|
|
static void ilmi_do_state (void);
|
|
static void Increment_DL (int);
|
|
static void Decrement_DL (int);
|
|
|
|
static Snmp_Header * asn_get_header (u_char **);
|
|
static Snmp_Header * build_cold_start (void);
|
|
static Snmp_Header * build_generic_header (void);
|
|
|
|
/*
|
|
* Write a syslog() style timestamp
|
|
*
|
|
* Write a syslog() style timestamp with month, day, time and hostname
|
|
* to the log file.
|
|
*
|
|
* Arguments:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
write_timestamp (void)
|
|
{
|
|
time_t clk;
|
|
struct tm *tm;
|
|
|
|
clk = time ( (time_t)NULL );
|
|
tm = localtime ( &clk );
|
|
|
|
if ( Log && Debug_Level > 1 )
|
|
if ( Log != stderr )
|
|
fprintf ( Log, "%.3s %2d %.2d:%.2d:%.2d %s: ",
|
|
Months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min,
|
|
tm->tm_sec, hostname );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Utility to pretty print buffer as hex dumps
|
|
*
|
|
* Arguments:
|
|
* out - file handle
|
|
* ptr - buffer pointer
|
|
* len - length to pretty print
|
|
* desc - output header
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
hexdump (out, ptr, len, desc)
|
|
FILE * out;
|
|
u_int8_t * ptr;
|
|
int len;
|
|
char * desc;
|
|
{
|
|
char line[17];
|
|
int i, j;
|
|
|
|
if (out == NULL)
|
|
out = stdout;
|
|
|
|
if (desc != NULL)
|
|
fprintf(out, "[ %s (%d bytes)]\n", desc, len);
|
|
|
|
bzero(line, sizeof(line));
|
|
|
|
for (i = 0, j = 0; i < len; i++) {
|
|
|
|
if (j == 0) fprintf(out, "%04x: ", i);
|
|
if (j == 8) fprintf(out, "| ");
|
|
|
|
fprintf(out, "%02x ", ptr[i]);
|
|
line[j] = isalnum(ptr[i]) ? ptr[i] : '.' ;
|
|
if (j == 15) {
|
|
fprintf(out, " |%16s|\n", line);
|
|
bzero(line, sizeof(line));
|
|
j = 0;
|
|
} else
|
|
j++;
|
|
}
|
|
|
|
if (j != 0) {
|
|
if (j <= 8) fprintf(out, " ");
|
|
for (; j < 16; j++) fprintf(out, " ");
|
|
fprintf(out, " |%-16s|\n", line);
|
|
}
|
|
fflush(out);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get lengths from PDU encodings
|
|
*
|
|
* Lengths are sometimes encoded as a single byte if the length
|
|
* is less the 127 but are more commonly encoded as one byte with
|
|
* the high bit set and the lower seven bits indicating the nuber
|
|
* of bytes which make up the length value. Trailing data is (to my
|
|
* knowledge) not 7-bit encoded.
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to buffer pointer
|
|
* plen - pointer to PDU length or NULL if not a concern
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* plen - (possibly) adjusted pdu length
|
|
* <len> - decoded length
|
|
*
|
|
*/
|
|
static int
|
|
asn_get_pdu_len (u_char **bufp, int *plen)
|
|
{
|
|
u_char *bp = *bufp;
|
|
int len = 0;
|
|
int i, b;
|
|
|
|
b = *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
if ( b & 0x80 ) {
|
|
for ( i = 0; i < (b & ~0x80); i++ ) {
|
|
len = len * 256 + *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
}
|
|
} else
|
|
len = b;
|
|
|
|
*bufp = bp;
|
|
return ( len );
|
|
}
|
|
|
|
/*
|
|
* Get an 7-bit encoded value.
|
|
*
|
|
* Get a value which is represented using a 7-bit encoding. The last
|
|
* byte in the stream has the high-bit clear.
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to the buffer pointer
|
|
* len - pointer to the buffer length
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* len - updated buffer length
|
|
* <val> - value encoding represented
|
|
*
|
|
*/
|
|
static int
|
|
asn_get_encoded (u_char **bufp, int *len)
|
|
{
|
|
u_char *bp = *bufp;
|
|
int val = 0;
|
|
int l = *len;
|
|
|
|
/*
|
|
* Keep going while high bit is set
|
|
*/
|
|
do {
|
|
/*
|
|
* Each byte can represent 7 bits
|
|
*/
|
|
val = ( val << 7 ) + ( *bp & ~0x80 );
|
|
l--;
|
|
} while ( *bp++ & 0x80 );
|
|
|
|
*bufp = bp; /* update buffer pointer */
|
|
*len = l; /* update buffer length */
|
|
|
|
return ( val );
|
|
}
|
|
|
|
/*
|
|
* Get a BER encoded integer
|
|
*
|
|
* Intergers are encoded as one byte length followed by <length> data bytes
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to the buffer pointer
|
|
* plen - pointer to PDU length or NULL if not a concern
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* plen - (possibly) updated PDU length
|
|
* <val> - value of encoded integer
|
|
*
|
|
*/
|
|
static int
|
|
asn_get_int (u_char **bufp, int *plen)
|
|
{
|
|
int i;
|
|
int len;
|
|
int v = 0;
|
|
u_char *bp = *bufp;
|
|
|
|
len = *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
for ( i = 0; i < len; i++ ) {
|
|
v = (v * 256) + *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
}
|
|
*bufp = bp;
|
|
return ( v );
|
|
}
|
|
|
|
/*
|
|
* Set a BER encoded integer
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to buffer pointer where we are to set int in
|
|
* val - integer value to set
|
|
*
|
|
* Returns:
|
|
* none
|
|
* <bufp> - updated buffer pointer
|
|
*
|
|
*/
|
|
static void
|
|
asn_set_int (u_char **bufp, int val)
|
|
{
|
|
union {
|
|
int i;
|
|
u_char c[4];
|
|
} u;
|
|
int len = sizeof(int);
|
|
size_t i = 0;
|
|
u_char *bp = *bufp;
|
|
|
|
/* Check for special case where val == 0 */
|
|
if ( val == 0 ) {
|
|
*bp++ = 1;
|
|
*bp++ = 0;
|
|
*bufp = bp;
|
|
return;
|
|
}
|
|
|
|
u.i = htonl ( val );
|
|
|
|
while ( u.c[i] == 0 && i++ < sizeof(int) )
|
|
len--;
|
|
|
|
if ( u.c[i] > 0x7f ) {
|
|
i--;
|
|
len++;
|
|
}
|
|
|
|
*bp++ = len;
|
|
bcopy ( (caddr_t)&u.c[sizeof(int)-len], bp, len );
|
|
bp += len;
|
|
*bufp = bp;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Utility to print an object identifier.
|
|
*
|
|
* Arguments:
|
|
* objid - pointer to objid representation
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
print_objid (Objid *objid)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* First oid coded as 40 * X + Y
|
|
*/
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, ".%d.%d", objid->oid[1] / 40,
|
|
objid->oid[1] % 40 );
|
|
}
|
|
for ( i = 2; i <= objid->oid[0]; i++ )
|
|
if ( Log )
|
|
fprintf ( Log, ".%d", objid->oid[i] );
|
|
if ( Log )
|
|
fprintf ( Log, "\n" );
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get Object Identifier
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to buffer pointer
|
|
* objid - pointer to objid buffer
|
|
* plen - pointer to PDU length or NULL of not a concern
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* objid - internal representation of encoded objid
|
|
* plen - (possibly) adjusted PDU length
|
|
*
|
|
*/
|
|
static void
|
|
asn_get_objid (u_char **bufp, Objid *objid, int *plen)
|
|
{
|
|
int len;
|
|
u_char *bp = *bufp;
|
|
int *ip = (int *)objid + 1; /* First byte will contain length */
|
|
int oidlen = 0;
|
|
|
|
len = *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
while ( len ) {
|
|
*ip++ = asn_get_encoded ( &bp, &len );
|
|
if ( plen )
|
|
(*plen)--;
|
|
oidlen++;
|
|
}
|
|
objid->oid[0] = oidlen;
|
|
*bufp = bp;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Put OBJID - assumes elements <= 16383 for two byte coding
|
|
*
|
|
*/
|
|
static int
|
|
asn_put_objid (u_char **bufp, Objid *objid)
|
|
{
|
|
int len = 0;
|
|
u_char *bp = *bufp;
|
|
u_char *cpp;
|
|
int i;
|
|
|
|
cpp = bp;
|
|
*bp++ = objid->oid[0];
|
|
len++;
|
|
for ( i = 1; i <= objid->oid[0]; i++ ) {
|
|
u_int c = objid->oid[i];
|
|
|
|
while ( c > 127 ) {
|
|
*bp++ = ( ( c >> 7 ) & 0x7f ) | 0x80;
|
|
len++;
|
|
c &= 0x7f; /* XXX - assumption of two bytes */
|
|
(*cpp)++;
|
|
}
|
|
*bp++ = c;
|
|
len++;
|
|
}
|
|
|
|
*bufp = bp;
|
|
return ( len );
|
|
|
|
}
|
|
|
|
/*
|
|
* Get OCTET STRING
|
|
*
|
|
* Octet strings are encoded as a 7-bit encoded length followed by <len>
|
|
* data bytes;
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to buffer pointer
|
|
* octet - pointer to octet buffer
|
|
* plen - pointer to PDU length
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* octet - encoded Octet String
|
|
* plen - (possibly) adjusted PDU length
|
|
*
|
|
*/
|
|
static void
|
|
asn_get_octet (u_char **bufp, char *octet, int *plen)
|
|
{
|
|
u_char *bp = *bufp;
|
|
int i = 0;
|
|
int len = 0;
|
|
|
|
/*
|
|
* &i is really a dummy value here as we don't keep track
|
|
* of the ongoing buffer length
|
|
*/
|
|
len = asn_get_encoded ( &bp, &i );
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
*octet++ = *bp++;
|
|
if ( plen )
|
|
(*plen)--;
|
|
}
|
|
|
|
*bufp = bp;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Utility to print SNMP PDU header information
|
|
*
|
|
* Arguments:
|
|
* Hdr - pointer to internal SNMP header structure
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
print_header (Snmp_Header *Hdr)
|
|
{
|
|
Variable *var;
|
|
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf(Log,
|
|
" PDU Type: 0x%x (%s)\n"
|
|
" PDU len: %d\n"
|
|
" Version: %d\n"
|
|
" Community: \"%s\"\n",
|
|
Hdr->pdutype, PDU_Types[Hdr->pdutype & 7],
|
|
Hdr->pdulen,
|
|
Hdr->version + 1,
|
|
Hdr->community);
|
|
|
|
if (Hdr->pdutype != PDU_TYPE_TRAP) {
|
|
write_timestamp();
|
|
fprintf(Log,
|
|
" Req Id: 0x%x\n"
|
|
" Error: %d\n"
|
|
" Error Index: %d\n",
|
|
Hdr->reqid,
|
|
Hdr->error,
|
|
Hdr->erridx);
|
|
}
|
|
}
|
|
|
|
var = Hdr->head;
|
|
while ( var ) {
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "Variable Type: %d", var->type );
|
|
if ( Var_Types[var->type] )
|
|
fprintf ( Log, " (%s)", Var_Types[var->type] );
|
|
fprintf ( Log, "\n Object: ");
|
|
print_objid ( &var->oid );
|
|
fprintf ( Log, " Value: ");
|
|
switch ( var->type ) {
|
|
case ASN_INTEGER:
|
|
fprintf ( Log, "%d (0x%x)\n", var->var.ival, var->var.ival );
|
|
break;
|
|
case ASN_NULL:
|
|
fprintf ( Log, "NULL" );
|
|
break;
|
|
default:
|
|
fprintf ( Log, "[0x%x]", var->type );
|
|
break;
|
|
}
|
|
fprintf ( Log, "\n" );
|
|
}
|
|
var = var->next;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Pull OID's from GET/SET message
|
|
*
|
|
* Arguments:
|
|
* h - pointer to Snmp_Header
|
|
* bp - pointer to input PDU
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
parse_oids (Snmp_Header *h, u_char **bp)
|
|
{
|
|
int len = h->varlen;
|
|
int sublen;
|
|
Variable *var;
|
|
u_char *bufp = *bp;
|
|
|
|
while ( len > 0 ) {
|
|
if ( *bufp++ == ASN_SEQUENCE ) {
|
|
len--;
|
|
|
|
/* Create new Variable instance */
|
|
if ( ( var = (Variable *)malloc(sizeof(Variable)) ) == NULL )
|
|
{
|
|
*bp = bufp;
|
|
return;
|
|
}
|
|
bzero(var, sizeof(Variable));
|
|
/* Link to tail */
|
|
if ( h->tail )
|
|
h->tail->next = var;
|
|
/* Set head iff NULL */
|
|
if ( h->head == NULL ) {
|
|
h->head = var;
|
|
}
|
|
/* Adjust tail */
|
|
h->tail = var;
|
|
|
|
/* Get length of variable sequence */
|
|
sublen = asn_get_pdu_len ( &bufp, &len );
|
|
/* Should be OBJID type */
|
|
if ( *bufp++ != ASN_OBJID ) {
|
|
*bp = bufp;
|
|
return;
|
|
}
|
|
asn_get_objid ( (u_char **)&bufp, &var->oid, &len );
|
|
var->type = *bufp++;
|
|
len--;
|
|
switch ( var->type ) {
|
|
case ASN_INTEGER:
|
|
var->var.ival = asn_get_int ( &bufp, &len );
|
|
break;
|
|
case ASN_NULL:
|
|
bufp++;
|
|
len--;
|
|
break;
|
|
case ASN_OBJID:
|
|
asn_get_objid ( &bufp, &var->var.oval, &len );
|
|
break;
|
|
case ASN_OCTET:
|
|
asn_get_octet ( &bufp, var->var.sval, &len );
|
|
break;
|
|
default:
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "Unknown variable type: %d\n",
|
|
var->type );
|
|
}
|
|
break;
|
|
}
|
|
var->next = NULL;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
*bp = bufp;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Crack the SNMP header
|
|
*
|
|
* Pull the PDU length, SNMP version, SNMP community and PDU type.
|
|
* If present, also pull out the Request ID, Error status, and Error
|
|
* index values.
|
|
*
|
|
* Arguments:
|
|
* bufp - pointer to buffer pointer
|
|
*
|
|
* Returns:
|
|
* bufp - updated buffer pointer
|
|
* - generated SNMP header
|
|
*
|
|
*/
|
|
static Snmp_Header *
|
|
asn_get_header (u_char **bufp)
|
|
{
|
|
Snmp_Header *h;
|
|
u_char *bp = *bufp;
|
|
int len = 0;
|
|
int dummy = 0;
|
|
|
|
/*
|
|
* Allocate memory to hold the SNMP header
|
|
*/
|
|
if ( ( h = (Snmp_Header *)malloc(sizeof(Snmp_Header)) ) == NULL )
|
|
return ( (Snmp_Header *)NULL );
|
|
|
|
/*
|
|
* Ensure that we wipe the slate clean
|
|
*/
|
|
bzero(h, sizeof(Snmp_Header));
|
|
|
|
/*
|
|
* PDU has to start as SEQUENCE OF
|
|
*/
|
|
if ( *bp++ != ASN_SEQUENCE ) /* Class == Universial, f == 1, tag == SEQUENCE */
|
|
return ( (Snmp_Header *)NULL );
|
|
|
|
/*
|
|
* Get the length of remaining PDU data
|
|
*/
|
|
h->pdulen = asn_get_pdu_len ( &bp, NULL );
|
|
|
|
/*
|
|
* We expect to find an integer encoding Version-1
|
|
*/
|
|
if ( *bp++ != ASN_INTEGER ) {
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
h->version = asn_get_int ( &bp, NULL );
|
|
|
|
/*
|
|
* After the version, we need the community name
|
|
*/
|
|
if ( *bp++ != ASN_OCTET ) {
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
asn_get_octet ( &bp, h->community, NULL );
|
|
|
|
/*
|
|
* Single byte PDU type
|
|
*/
|
|
h->pdutype = *bp++;
|
|
|
|
/*
|
|
* If this isn't a TRAP PDU, then look for the rest of the header
|
|
*/
|
|
if ( h->pdutype != PDU_TYPE_TRAP ) { /* TRAP uses different format */
|
|
|
|
(void) asn_get_pdu_len ( &bp, &dummy );
|
|
|
|
/* Request ID */
|
|
if ( *bp++ != ASN_INTEGER ) {
|
|
free( h );
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
h->reqid = asn_get_int ( &bp, NULL );
|
|
|
|
/* Error Status */
|
|
if ( *bp++ != ASN_INTEGER ) {
|
|
free ( h );
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
h->error = asn_get_int ( &bp, NULL );
|
|
|
|
/* Error Index */
|
|
if ( *bp++ != ASN_INTEGER ) {
|
|
free ( h );
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
h->erridx = asn_get_int ( &bp, NULL );
|
|
|
|
/* Sequence of... */
|
|
if ( *bp++ != ASN_SEQUENCE ) {
|
|
free ( h );
|
|
return ( (Snmp_Header *)NULL );
|
|
}
|
|
h->varlen = ( asn_get_pdu_len ( &bp, &len ) - 1 );
|
|
h->varlen += ( len - 1 );
|
|
|
|
parse_oids ( h, &bp );
|
|
}
|
|
|
|
*bufp = bp;
|
|
|
|
return ( h );
|
|
|
|
}
|
|
|
|
/*
|
|
* Compare two internal OID representations
|
|
*
|
|
* Arguments:
|
|
* oid1 - Internal Object Identifier
|
|
* oid2 - Internal Object Identifier
|
|
*
|
|
* Returns:
|
|
* 0 - Objid's match
|
|
* 1 - Objid's don't match
|
|
*
|
|
*/
|
|
static int
|
|
oid_cmp (Objid *oid1, Objid *oid2)
|
|
{
|
|
int i;
|
|
int len;
|
|
|
|
/*
|
|
* Compare lengths
|
|
*/
|
|
if ( !(oid1->oid[0] == oid2->oid[0] ) )
|
|
/* Different lengths */
|
|
return ( 1 );
|
|
|
|
len = oid1->oid[0];
|
|
|
|
/*
|
|
* value by value compare
|
|
*/
|
|
for ( i = 1; i <= len; i++ ) {
|
|
if ( !(oid1->oid[i] == oid2->oid[i]) )
|
|
/* values don't match */
|
|
return ( 1 );
|
|
}
|
|
|
|
/* Objid's are identical */
|
|
return ( 0 );
|
|
}
|
|
|
|
/*
|
|
* Compare two internal OID representations
|
|
*
|
|
* Arguments:
|
|
* oid1 - Internal Object Identifier
|
|
* oid2 - Internal Object Identifier
|
|
* len - Length of OID to compare
|
|
*
|
|
* Returns:
|
|
* 0 - Objid's match
|
|
* 1 - Objid's don't match
|
|
*
|
|
*/
|
|
static int
|
|
oid_ncmp (Objid *oid1, Objid *oid2, int len)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* value by value compare
|
|
*/
|
|
for ( i = 1; i <= len; i++ ) {
|
|
if ( !(oid1->oid[i] == oid2->oid[i]) )
|
|
/* values don't match */
|
|
return ( 1 );
|
|
}
|
|
|
|
/* Objid's are identical */
|
|
return ( 0 );
|
|
}
|
|
|
|
/*
|
|
* Find the index of an OBJID which matches this Variable instance.
|
|
*
|
|
* Arguments:
|
|
* var - pointer to Variable instance
|
|
*
|
|
* Returns:
|
|
* idx - index of matched Variable instance
|
|
* -1 - no matching Variable found
|
|
*
|
|
*/
|
|
static int
|
|
find_var (Variable *var)
|
|
{
|
|
size_t i;
|
|
|
|
for ( i = 0; i < NUM_OIDS; i++ )
|
|
if ( oid_cmp ( &var->oid, &Objids[i] ) == 0 ) {
|
|
return ( i );
|
|
}
|
|
|
|
return ( -1 );
|
|
|
|
}
|
|
|
|
/*
|
|
* Return the time process has been running as a number of ticks
|
|
*
|
|
* Arguments:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* number of ticks
|
|
*
|
|
*/
|
|
static int
|
|
get_ticks (void)
|
|
{
|
|
struct timeval timenow;
|
|
struct timeval timediff;
|
|
|
|
(void) gettimeofday ( &timenow, NULL );
|
|
/*
|
|
* Adjust for subtraction
|
|
*/
|
|
timenow.tv_sec--;
|
|
timenow.tv_usec += 1000000;
|
|
|
|
/*
|
|
* Compute time since 'starttime'
|
|
*/
|
|
timediff.tv_sec = timenow.tv_sec - starttime.tv_sec;
|
|
timediff.tv_usec = timenow.tv_usec - starttime.tv_usec;
|
|
|
|
/*
|
|
* Adjust difference timeval
|
|
*/
|
|
if ( timediff.tv_usec >= 1000000 ) {
|
|
timediff.tv_usec -= 1000000;
|
|
timediff.tv_sec++;
|
|
}
|
|
|
|
/*
|
|
* Compute number of ticks
|
|
*/
|
|
return ( ( timediff.tv_sec * 100 ) + ( timediff.tv_usec / 10000 ) );
|
|
|
|
}
|
|
|
|
/*
|
|
* Build a response PDU
|
|
*
|
|
* Arguments:
|
|
* hdr - pointer to PDU Header with completed Variable list
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
build_pdu (Snmp_Header *hdr, int type)
|
|
{
|
|
u_char *bp = Resp_Buf;
|
|
u_char *vpp;
|
|
u_char *ppp;
|
|
int erridx = 0;
|
|
int varidx = 1;
|
|
int varlen = 0;
|
|
int pdulen = 0;
|
|
int traplen = 0;
|
|
Variable *var;
|
|
|
|
/*
|
|
* Clear out the reply
|
|
*/
|
|
bzero ( Resp_Buf, sizeof(Resp_Buf) );
|
|
|
|
/* [0] is reserved for overall length */
|
|
bp++;
|
|
|
|
/* Start with SEQUENCE OF */
|
|
*bp++ = ASN_SEQUENCE;
|
|
/* - assume we can code length in two octets */
|
|
*bp++ = 0x82;
|
|
bp++;
|
|
bp++;
|
|
/* Version */
|
|
*bp++ = ASN_INTEGER;
|
|
asn_set_int ( &bp, hdr->version );
|
|
/* Community name */
|
|
*bp++ = ASN_OCTET;
|
|
*bp++ = strlen ( hdr->community );
|
|
bcopy ( hdr->community, bp, strlen ( hdr->community ) );
|
|
bp += strlen ( hdr->community );
|
|
/* PDU Type */
|
|
*bp++ = type;
|
|
ppp = bp;
|
|
/* Length of OID data - assume it'll fit in one octet */
|
|
bp++;
|
|
|
|
if ( type != PDU_TYPE_TRAP ) {
|
|
/* Sequence ID */
|
|
*bp++ = ASN_INTEGER;
|
|
asn_set_int ( &bp, hdr->reqid );
|
|
/*
|
|
* Check to see if all the vaiables were resolved - we do this
|
|
* by looking for something which still has an ASN_NULL value.
|
|
*/
|
|
var = hdr->head;
|
|
if ( type == PDU_TYPE_GETRESP ) {
|
|
while ( var && erridx == 0 ) {
|
|
if ( var->type != ASN_NULL ) {
|
|
varidx++;
|
|
var = var->next;
|
|
} else
|
|
erridx = varidx;
|
|
}
|
|
}
|
|
|
|
/* Error status */
|
|
*bp++ = ASN_INTEGER;
|
|
*bp++ = 0x01; /* length = 1 */
|
|
if ( erridx )
|
|
*bp++ = SNMP_ERR_NOSUCHNAME;
|
|
else
|
|
*bp++ = SNMP_ERR_NOERROR;
|
|
/* Error Index */
|
|
*bp++ = ASN_INTEGER;
|
|
*bp++ = 0x01; /* length = 1 */
|
|
*bp++ = erridx; /* index == 0 if no error */
|
|
} else {
|
|
/* type == PDU_TYPE_TRAP */
|
|
|
|
/* Fill in ENTERPRISE OBJID */
|
|
*bp++ = ASN_OBJID;
|
|
(void) asn_put_objid ( &bp, &hdr->enterprise );
|
|
|
|
/* Fill in IP address */
|
|
*bp++ = ASN_IPADDR;
|
|
*bp++ = sizeof ( hdr->ipaddr );
|
|
bcopy ( (caddr_t)&hdr->ipaddr, bp, sizeof(hdr->ipaddr) );
|
|
bp += sizeof(hdr->ipaddr);
|
|
|
|
/* Fill in generic and specific trap types */
|
|
*bp++ = ASN_INTEGER;
|
|
asn_set_int ( &bp, hdr->generic_trap );
|
|
*bp++ = ASN_INTEGER;
|
|
asn_set_int ( &bp, hdr->specific_trap );
|
|
|
|
/* Fill in time-stamp - assume 0 for now */
|
|
*bp++ = ASN_TIMESTAMP;
|
|
asn_set_int ( &bp, 0 );
|
|
|
|
/* encoded length */
|
|
traplen = ( bp - ppp - 1 );
|
|
|
|
/* Continue with variable processing */
|
|
}
|
|
|
|
/* SEQUENCE OF */
|
|
*bp++ = ASN_SEQUENCE;
|
|
*bp++ = 0x82;
|
|
/* - assume we can code length in two octets */
|
|
vpp = bp;
|
|
varlen = 0;
|
|
bp++;
|
|
bp++;
|
|
|
|
/* Install Variables */
|
|
var = hdr->head;
|
|
varidx = 1;
|
|
while ( var ) {
|
|
u_char *bpp;
|
|
int len = 0;
|
|
|
|
/* SEQUENCE OF */
|
|
*bp++ = ASN_SEQUENCE;
|
|
*bp++ = 0x82;
|
|
/* - assume we can code length in two octets */
|
|
bpp = bp;
|
|
bp++;
|
|
bp++;
|
|
/* OBJID */
|
|
*bp++ = ASN_OBJID;
|
|
len++;
|
|
|
|
len += asn_put_objid ( &bp, &var->oid );
|
|
|
|
if ( erridx && varidx >= erridx ) {
|
|
/* Code this variable as NULL */
|
|
*bp++ = ASN_NULL;
|
|
len++;
|
|
bp++;
|
|
len++;
|
|
} else {
|
|
u_char *lpp;
|
|
/* Variable type */
|
|
*bp++ = var->type;
|
|
len++;
|
|
lpp = bp;
|
|
switch ( var->type ) {
|
|
case ASN_INTEGER:
|
|
asn_set_int ( &bp, var->var.ival );
|
|
len += ( *lpp + 1 );
|
|
break;
|
|
case ASN_OCTET:
|
|
*bp++ = var->var.sval[0];
|
|
len++;
|
|
bcopy ( (caddr_t)&var->var.sval[1],
|
|
bp, var->var.sval[0] );
|
|
len += var->var.sval[0];
|
|
bp += var->var.sval[0];
|
|
break;
|
|
case ASN_NULL:
|
|
*bp++ = 0x00;
|
|
len++;
|
|
break;
|
|
case ASN_OBJID:
|
|
len += asn_put_objid ( &bp, &var->var.oval );
|
|
break;
|
|
case ASN_SEQUENCE:
|
|
break;
|
|
case ASN_IPADDR:
|
|
*bp++ = 4;
|
|
len++;
|
|
bcopy ( (caddr_t)&var->var.aval, bp, 4 );
|
|
len += 4;
|
|
bp += 4;
|
|
break;
|
|
case ASN_TIMESTAMP:
|
|
asn_set_int ( &bp, var->var.ival );
|
|
len += ( *lpp + 1 );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Accumulate total Variable sequence length */
|
|
varlen += (len + 4);
|
|
|
|
/* Fill in length of this sequence */
|
|
bpp[1] = len & 0xff;
|
|
bpp[0] = len >> 8;
|
|
|
|
var = var->next;
|
|
}
|
|
|
|
|
|
/* Fill in length of Variable sequence */
|
|
vpp[1] = varlen & 0xff;
|
|
vpp[0] = varlen >> 8;
|
|
|
|
if ( type != PDU_TYPE_TRAP ) {
|
|
/* Fill in length of data AFTER PDU type */
|
|
*ppp = varlen + 12 + ppp[2]; /* + length of reqid */
|
|
} else {
|
|
/* Fill in length of data AFTER PDU type */
|
|
*ppp = varlen + traplen + 4; /* + length of initial sequence of */
|
|
}
|
|
|
|
/* Fill in overall sequence length */
|
|
pdulen = *ppp + 7 + strlen ( hdr->community );
|
|
Resp_Buf[4] = pdulen & 0x7f;
|
|
Resp_Buf[3] = pdulen >> 8;
|
|
|
|
pdulen = bp - Resp_Buf - 1;
|
|
|
|
Resp_Buf[0] = pdulen;
|
|
|
|
hdr->pdutype = type;
|
|
hdr->pdulen = pdulen;
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
free_pdu (Snmp_Header *hdr)
|
|
{
|
|
Variable *var;
|
|
|
|
while ( hdr->head ) {
|
|
var = hdr->head->next; /* Save next link */
|
|
free ( hdr->head ); /* Free current var */
|
|
hdr->head = var; /* Set head to next link */
|
|
}
|
|
|
|
free ( hdr ); /* Free fixed portion */
|
|
}
|
|
|
|
static void
|
|
print_pdu (int dir, int intf, Snmp_Header *Hdr, int len, u_char *buf)
|
|
{
|
|
const char * pdu_dir;
|
|
const char * pdu_type;
|
|
int pdu_num;
|
|
|
|
write_timestamp();
|
|
|
|
switch (dir) {
|
|
case PDU_SEND:
|
|
pdu_dir = "SEND";
|
|
break;
|
|
case PDU_RECV:
|
|
pdu_dir = "RECV";
|
|
break;
|
|
default:
|
|
pdu_dir = "undefined";
|
|
break;
|
|
}
|
|
|
|
if (Hdr == NULL) {
|
|
pdu_type = "unknown";
|
|
pdu_num = 0;
|
|
} else {
|
|
pdu_type = PDU_Types[Hdr->pdutype & 7];
|
|
pdu_num = Hdr->pdutype;
|
|
}
|
|
|
|
fprintf(Log,
|
|
"%s: %s(%d), ILMI %s(%d), PDU Type %s(0x%x) %d/%d bytes.\n",
|
|
pdu_dir,
|
|
Intf[intf].anp_intf, ilmi_fd[intf],
|
|
ILMI_State[intf], ilmi_state[intf],
|
|
pdu_type, pdu_num,
|
|
len, buf[0]);
|
|
|
|
if (Hdr == NULL)
|
|
fprintf(Log, "Header seems to be invalid.\n");
|
|
else
|
|
print_header(Hdr);
|
|
|
|
hexdump(Log, (u_char *)&buf[1], len, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Send a generic response packet
|
|
*
|
|
* Arguments:
|
|
* sd - socket to send the reply on
|
|
* reqid - original request ID from GET PDU
|
|
* resp - pointer to the response to send
|
|
*
|
|
* Returns:
|
|
* none - response sent
|
|
*
|
|
*/
|
|
static void
|
|
send_resp (int intf, Snmp_Header *Hdr, u_char *resp)
|
|
{
|
|
int n;
|
|
|
|
if ( ilmi_fd[intf] > 0 ) {
|
|
n = write ( ilmi_fd[intf], (caddr_t)&resp[1], resp[0] );
|
|
if ( Log && Debug_Level > 1 ) {
|
|
print_pdu(PDU_SEND, intf, Hdr, n, resp);
|
|
}
|
|
}
|
|
|
|
free_pdu ( Hdr );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Build a COLD_START TRAP PDU
|
|
*
|
|
*/
|
|
static Snmp_Header *
|
|
build_cold_start (void)
|
|
{
|
|
Snmp_Header *hdr;
|
|
Variable *var;
|
|
|
|
hdr = (Snmp_Header *)malloc(sizeof(Snmp_Header));
|
|
if (hdr == NULL) {
|
|
fprintf(stderr, "malloc() failed in %s()\n", __func__);
|
|
exit(1);
|
|
}
|
|
bzero(hdr, sizeof(Snmp_Header));
|
|
|
|
hdr->pdulen = 0;
|
|
hdr->version = SNMP_VERSION_1 - 1;
|
|
snprintf ( hdr->community, sizeof(hdr->community), "ILMI" );
|
|
|
|
hdr->ipaddr = 0x0; /* 0.0.0.0 */
|
|
hdr->generic_trap = TRAP_COLDSTART;
|
|
hdr->specific_trap = 0;
|
|
bcopy ( (caddr_t)&Objids[ENTERPRISE_OBJID], (caddr_t)&hdr->enterprise,
|
|
sizeof(Objid) );
|
|
|
|
hdr->head = (Variable *)malloc(sizeof(Variable));
|
|
if (hdr == NULL) {
|
|
fprintf(stderr, "malloc() failed in %s()\n", __func__);
|
|
exit(1);
|
|
}
|
|
bzero(hdr->head, sizeof(Variable));
|
|
|
|
var = hdr->head;
|
|
bcopy ( (caddr_t)&Objids[UPTIME_OBJID], (caddr_t)&var->oid,
|
|
sizeof(Objid) );
|
|
var->type = ASN_NULL;
|
|
|
|
return ( hdr );
|
|
}
|
|
|
|
/*
|
|
* Build a Generic PDU Header
|
|
*
|
|
*/
|
|
static Snmp_Header *
|
|
build_generic_header (void)
|
|
{
|
|
Snmp_Header *hdr;
|
|
|
|
hdr = (Snmp_Header *)malloc(sizeof(Snmp_Header));
|
|
if (hdr == NULL) {
|
|
fprintf(stderr, "malloc() failed in %s()\n", __func__);
|
|
exit(1);
|
|
}
|
|
bzero(hdr, sizeof(Snmp_Header));
|
|
|
|
hdr->pdulen = 0;
|
|
hdr->version = SNMP_VERSION_1 - 1;
|
|
snprintf ( hdr->community, sizeof(hdr->community), "ILMI" );
|
|
|
|
return ( hdr );
|
|
}
|
|
|
|
/*
|
|
* Initialize information on what physical adapters HARP knows about
|
|
*
|
|
* Query the HARP subsystem about configuration and physical interface
|
|
* information for any currently registered ATM adapters. Store the information
|
|
* as arrays for easier indexing by SNMP port/index numbers.
|
|
*
|
|
* Arguments:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* none Information from HARP available
|
|
*
|
|
*/
|
|
static void
|
|
init_ilmi (void)
|
|
{
|
|
struct air_cfg_rsp *cfg_info = NULL;
|
|
struct air_int_rsp *intf_info = NULL;
|
|
int buf_len;
|
|
|
|
/*
|
|
* Get configuration info - what's available with 'atm sh config'
|
|
*/
|
|
buf_len = get_cfg_info ( NULL, &cfg_info );
|
|
/*
|
|
* If error occurred, clear out everything
|
|
*/
|
|
if ( buf_len <= 0 ) {
|
|
bzero ( Cfg, sizeof(Cfg) );
|
|
bzero( Intf, sizeof(Intf) );
|
|
NUnits = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Move to local storage
|
|
*/
|
|
bcopy ( cfg_info, (caddr_t)Cfg, buf_len );
|
|
/*
|
|
* Compute how many units information was returned for
|
|
*/
|
|
NUnits = buf_len / sizeof(struct air_cfg_rsp);
|
|
/* Housecleaning */
|
|
free ( cfg_info );
|
|
cfg_info = NULL;
|
|
/*
|
|
* Get the per interface information
|
|
*/
|
|
buf_len = get_intf_info ( NULL, &intf_info );
|
|
/*
|
|
* If error occurred, clear out Intf info
|
|
*/
|
|
if ( buf_len <= 0 ) {
|
|
bzero ( Intf, sizeof(Intf) );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Move to local storage
|
|
*/
|
|
bcopy ( intf_info, (caddr_t)Intf, buf_len );
|
|
/* Housecleaning */
|
|
free ( intf_info );
|
|
intf_info = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Open a new SNMP session for ILMI
|
|
*
|
|
* Start by updating interface information, in particular, how many
|
|
* interfaces are in the system. While we'll try to open sessons on
|
|
* all interfaces, this deamon currently can only handle the first
|
|
* interface.
|
|
*
|
|
* Arguments:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
ilmi_open (void)
|
|
{
|
|
struct sockaddr_atm satm;
|
|
struct t_atm_aal5 aal5;
|
|
struct t_atm_traffic traffic;
|
|
struct t_atm_bearer bearer;
|
|
struct t_atm_qos qos;
|
|
struct t_atm_app_name appname;
|
|
Atm_addr subaddr;
|
|
char nifname[IFNAMSIZ];
|
|
int optlen;
|
|
int unit = 0;
|
|
u_char sig_proto;
|
|
|
|
init_ilmi();
|
|
|
|
for ( unit = 0; unit < NUnits; unit++ ) {
|
|
|
|
/*
|
|
* ILMI only makes sense for UNI signalling protocols
|
|
*/
|
|
sig_proto = Intf[unit].anp_sig_proto;
|
|
if ( sig_proto != ATM_SIG_UNI30 && sig_proto != ATM_SIG_UNI31 &&
|
|
sig_proto != ATM_SIG_UNI40 )
|
|
continue;
|
|
|
|
if ( ilmi_fd[unit] == -1 ) {
|
|
|
|
ilmi_fd[unit] = socket ( AF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5 );
|
|
|
|
if ( ilmi_fd[unit] < 0 ) {
|
|
perror ( "open" );
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Set interface name. For now, we must have a netif to go on...
|
|
*/
|
|
if ( Intf[unit].anp_nif_cnt == 0 ) {
|
|
if ( Debug_Level > 1 && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "No nif on unit %d\n", unit );
|
|
}
|
|
close ( ilmi_fd[unit] );
|
|
ilmi_fd[unit] = -1;
|
|
continue;
|
|
}
|
|
sprintf ( nifname, "%s0", Intf[unit].anp_nif_pref );
|
|
optlen = sizeof ( nifname );
|
|
if ( setsockopt ( ilmi_fd[unit], T_ATM_SIGNALING,
|
|
T_ATM_NET_INTF, (caddr_t)nifname, optlen ) < 0 ) {
|
|
perror ( "setsockopt" );
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log,
|
|
"Couldn't set interface name \"%s\"\n",
|
|
nifname );
|
|
}
|
|
if ( Debug_Level > 1 && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "nifname: closing unit %d\n", unit );
|
|
}
|
|
close ( ilmi_fd[unit] );
|
|
ilmi_fd[unit] = -1;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Set up destination SAP
|
|
*/
|
|
bzero ( (caddr_t) &satm, sizeof(satm) );
|
|
satm.satm_family = AF_ATM;
|
|
#if (defined(BSD) && (BSD >= 199103))
|
|
satm.satm_len = sizeof(satm);
|
|
#endif
|
|
|
|
satm.satm_addr.t_atm_sap_addr.SVE_tag_addr = T_ATM_PRESENT;
|
|
satm.satm_addr.t_atm_sap_addr.SVE_tag_selector = T_ATM_ABSENT;
|
|
satm.satm_addr.t_atm_sap_addr.address_format = T_ATM_PVC_ADDR;
|
|
satm.satm_addr.t_atm_sap_addr.address_length = sizeof(Atm_addr_pvc);
|
|
ATM_PVC_SET_VPI((Atm_addr_pvc *)satm.satm_addr.t_atm_sap_addr.address,
|
|
0 );
|
|
ATM_PVC_SET_VCI((Atm_addr_pvc *)satm.satm_addr.t_atm_sap_addr.address,
|
|
16 );
|
|
|
|
satm.satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_PRESENT;
|
|
satm.satm_addr.t_atm_sap_layer2.ID_type = T_ATM_SIMPLE_ID;
|
|
satm.satm_addr.t_atm_sap_layer2.ID.simple_ID = T_ATM_BLLI2_I8802;
|
|
|
|
satm.satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
|
|
|
|
satm.satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
|
|
|
|
/*
|
|
* Set up connection parameters
|
|
*/
|
|
aal5.forward_max_SDU_size = MAX_LEN;
|
|
aal5.backward_max_SDU_size = MAX_LEN;
|
|
aal5.SSCS_type = T_ATM_NULL;
|
|
optlen = sizeof(aal5);
|
|
if ( setsockopt ( ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_AAL5,
|
|
(caddr_t) &aal5, optlen ) < 0 ) {
|
|
perror ( "setsockopt(aal5)" );
|
|
if ( Debug_Level > 1 && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "aal5: closing unit %d\n", unit );
|
|
}
|
|
close ( ilmi_fd[unit] );
|
|
ilmi_fd[unit] = -1;
|
|
continue;
|
|
}
|
|
|
|
traffic.forward.PCR_high_priority = T_ATM_ABSENT;
|
|
traffic.forward.PCR_all_traffic = 100000;
|
|
traffic.forward.SCR_high_priority = T_ATM_ABSENT;
|
|
traffic.forward.SCR_all_traffic = T_ATM_ABSENT;
|
|
traffic.forward.MBS_high_priority = T_ATM_ABSENT;
|
|
traffic.forward.MBS_all_traffic = T_ATM_ABSENT;
|
|
traffic.forward.tagging = T_NO;
|
|
traffic.backward.PCR_high_priority = T_ATM_ABSENT;
|
|
traffic.backward.PCR_all_traffic = 100000;
|
|
traffic.backward.SCR_high_priority = T_ATM_ABSENT;
|
|
traffic.backward.SCR_all_traffic = T_ATM_ABSENT;
|
|
traffic.backward.MBS_high_priority = T_ATM_ABSENT;
|
|
traffic.backward.MBS_all_traffic = T_ATM_ABSENT;
|
|
traffic.backward.tagging = T_NO;
|
|
traffic.best_effort = T_YES;
|
|
optlen = sizeof(traffic);
|
|
if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_TRAFFIC,
|
|
(caddr_t)&traffic, optlen) < 0) {
|
|
perror("setsockopt(traffic)");
|
|
}
|
|
bearer.bearer_class = T_ATM_CLASS_X;
|
|
bearer.traffic_type = T_ATM_NULL;
|
|
bearer.timing_requirements = T_ATM_NULL;
|
|
bearer.clipping_susceptibility = T_NO;
|
|
bearer.connection_configuration = T_ATM_1_TO_1;
|
|
optlen = sizeof(bearer);
|
|
if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_BEARER_CAP,
|
|
(caddr_t)&bearer, optlen) < 0) {
|
|
perror("setsockopt(bearer)");
|
|
}
|
|
|
|
qos.coding_standard = T_ATM_NETWORK_CODING;
|
|
qos.forward.qos_class = T_ATM_QOS_CLASS_0;
|
|
qos.backward.qos_class = T_ATM_QOS_CLASS_0;
|
|
optlen = sizeof(qos);
|
|
if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_QOS, (caddr_t)&qos,
|
|
optlen) < 0) {
|
|
perror("setsockopt(qos)");
|
|
}
|
|
|
|
subaddr.address_format = T_ATM_ABSENT;
|
|
subaddr.address_length = 0;
|
|
optlen = sizeof(subaddr);
|
|
if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_DEST_SUB,
|
|
(caddr_t)&subaddr, optlen) < 0) {
|
|
perror("setsockopt(dest_sub)");
|
|
}
|
|
|
|
strncpy(appname.app_name, "ILMI", T_ATM_APP_NAME_LEN);
|
|
optlen = sizeof(appname);
|
|
if (setsockopt(ilmi_fd[unit], T_ATM_SIGNALING, T_ATM_APP_NAME,
|
|
(caddr_t)&appname, optlen) < 0) {
|
|
perror("setsockopt(appname)");
|
|
}
|
|
|
|
/*
|
|
* Now try to connect to destination
|
|
*/
|
|
if ( connect ( ilmi_fd[unit], (struct sockaddr *) &satm,
|
|
sizeof(satm)) < 0 ) {
|
|
perror ( "connect" );
|
|
if ( Debug_Level > 1 && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "connect: closing unit %d\n", unit );
|
|
}
|
|
close ( ilmi_fd[unit] );
|
|
ilmi_fd[unit] = -1;
|
|
continue;
|
|
}
|
|
|
|
if ( Debug_Level && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "***** opened unit %d\n", unit );
|
|
}
|
|
|
|
ilmi_state[unit] = ILMI_COLDSTART;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Get our local IP address for this interface
|
|
*
|
|
* Arguments:
|
|
* s - socket to find address for
|
|
* aval - pointer to variable to store address in
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
get_local_ip (int s, long *aval)
|
|
{
|
|
char intf_name[IFNAMSIZ];
|
|
int namelen = IFNAMSIZ;
|
|
struct air_netif_rsp *net_info = NULL;
|
|
struct sockaddr_in *sain;
|
|
|
|
/*
|
|
* Get physical interface name
|
|
*/
|
|
if ( getsockopt ( s, T_ATM_SIGNALING, T_ATM_NET_INTF,
|
|
(caddr_t) intf_name, &namelen ) )
|
|
return;
|
|
|
|
/*
|
|
* Get network interface information for this physical interface
|
|
*/
|
|
get_netif_info ( intf_name, &net_info );
|
|
if ( net_info == NULL )
|
|
return;
|
|
|
|
sain = (struct sockaddr_in *)(void *)&net_info->anp_proto_addr;
|
|
|
|
/*
|
|
* Fill in answer
|
|
*/
|
|
bcopy ( (caddr_t)&sain->sin_addr.s_addr, aval, 4 );
|
|
|
|
free ( net_info );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
* Set local NSAP prefix and then reply with our full NSAP address.
|
|
*
|
|
* Switch will send a SET message with the NSAP prefix after a coldStart.
|
|
* We'll set that prefix into HARP and then send a SET message of our own
|
|
* with our full interface NSAP address.
|
|
*
|
|
* Arguments:
|
|
* oid - objid from SET message
|
|
* hdr - pointer to internal SNMP header
|
|
* buf - pointer to SET buffer
|
|
* s - socket to send messages on
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
set_prefix (Objid *oid, Snmp_Header *hdr __unused, int intf)
|
|
{
|
|
struct atmsetreq asr;
|
|
Atm_addr *aa;
|
|
int fd;
|
|
int i;
|
|
|
|
/*
|
|
* Build IOCTL request to set prefix
|
|
*/
|
|
asr.asr_opcode = AIOCS_SET_PRF;
|
|
strncpy ( asr.asr_prf_intf, Intf[intf].anp_intf,
|
|
sizeof(asr.asr_prf_intf ) );
|
|
/*
|
|
* Pull prefix out of received Objid
|
|
* save in set_prefix IOCTL and addressEntry table
|
|
*/
|
|
for ( i = 0; i < oid->oid[13]; i++ ) {
|
|
asr.asr_prf_pref[i] = oid->oid[i + 14];
|
|
}
|
|
|
|
/*
|
|
* Pass new prefix to the HARP kernel
|
|
*/
|
|
fd = socket ( AF_ATM, SOCK_DGRAM, 0 );
|
|
if ( fd < 0 )
|
|
return;
|
|
if ( ioctl ( fd, AIOCSET, (caddr_t)&asr ) < 0 ) {
|
|
if ( errno != EALREADY ) {
|
|
syslog ( LOG_ERR, "ilmid: error setting prefix: %m" );
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "errno %d setting prefix\n",
|
|
errno );
|
|
}
|
|
close ( fd );
|
|
return;
|
|
}
|
|
}
|
|
close ( fd );
|
|
|
|
/*
|
|
* Reload the cfg/intf info with newly set prefix
|
|
*/
|
|
init_ilmi();
|
|
|
|
aa = &Intf[intf].anp_addr;
|
|
|
|
/*
|
|
* Copy our NSAP into addressEntry table
|
|
*/
|
|
|
|
addressEntry[intf].oid[0] = 0;
|
|
for ( i = 0; i < aa->address_length; i++ ) {
|
|
addressEntry[intf].oid[0]++; /* Increment length */
|
|
addressEntry[intf].oid[i + 1] = (int)((u_char *)(aa->address))[i];
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
static void
|
|
set_address (Snmp_Header *hdr __unused, int intf)
|
|
{
|
|
Variable *var;
|
|
int i, j;
|
|
|
|
PDU_Header = build_generic_header();
|
|
|
|
PDU_Header->head = (Variable *)malloc(sizeof(Variable));
|
|
if (PDU_Header->head == NULL) {
|
|
fprintf(stderr, "malloc() failed in %s()\n", __func__);
|
|
exit(1);
|
|
}
|
|
bzero(PDU_Header->head, sizeof(Variable));
|
|
|
|
var = PDU_Header->head;
|
|
/* Copy generic addressEntry OBJID */
|
|
bcopy ( (caddr_t)&Objids[ADDRESS_OBJID], (caddr_t)&var->oid,
|
|
sizeof(Objid) );
|
|
/* Set specific instance */
|
|
i = var->oid.oid[0] + 1; /* Get length */
|
|
var->oid.oid[i++] = 1;
|
|
var->oid.oid[i++] = 1;
|
|
var->oid.oid[i++] = 3;
|
|
var->oid.oid[i++] = 0;
|
|
|
|
/* Copy in address length */
|
|
var->oid.oid[i++] = addressEntry[intf].oid[0];
|
|
|
|
/* Copy in address */
|
|
for ( j = 0; j < addressEntry[intf].oid[0]; j++ )
|
|
var->oid.oid[i++] = addressEntry[intf].oid[j + 1];
|
|
var->oid.oid[0] = i - 1; /* Set new length */
|
|
|
|
/* Set == VALID */
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = 1;
|
|
|
|
build_pdu ( PDU_Header, PDU_TYPE_SET );
|
|
send_resp ( intf, PDU_Header, Resp_Buf );
|
|
}
|
|
|
|
/*
|
|
* Increment Debug Level
|
|
*
|
|
* Catches SIGUSR1 signal and increments value of Debug_Level
|
|
*
|
|
* Arguments:
|
|
* sig - signal number
|
|
*
|
|
* Returns:
|
|
* none - Debug_Level incremented
|
|
*
|
|
*/
|
|
static void
|
|
Increment_DL (int sig __unused)
|
|
{
|
|
Debug_Level++;
|
|
if ( Debug_Level && Log == (FILE *)NULL ) {
|
|
if ( foregnd ) {
|
|
Log = stderr;
|
|
} else {
|
|
if ( ( Log = fopen ( LOG_FILE, "a" ) ) == NULL )
|
|
Log = NULL;
|
|
}
|
|
if ( Log ) {
|
|
setbuf ( Log, NULL );
|
|
write_timestamp();
|
|
fprintf ( Log, "Raised Debug_Level to %d\n", Debug_Level );
|
|
}
|
|
}
|
|
signal ( SIGUSR1, Increment_DL );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Decrement Debug Level
|
|
*
|
|
* Catches SIGUSR2 signal and decrements value of Debug_Level
|
|
*
|
|
* Arguments:
|
|
* sig - signal number
|
|
*
|
|
* Returns:
|
|
* none - Debug_Level decremented
|
|
*
|
|
*/
|
|
static void
|
|
Decrement_DL (int sig __unused)
|
|
{
|
|
Debug_Level--;
|
|
if ( Debug_Level <= 0 ) {
|
|
Debug_Level = 0;
|
|
if ( Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "Lowered Debug_Level to %d\n", Debug_Level );
|
|
if ( !foregnd )
|
|
fclose ( Log );
|
|
Log = NULL;
|
|
}
|
|
}
|
|
signal ( SIGUSR2, Decrement_DL );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Loop through GET variable list looking for matches
|
|
*
|
|
*/
|
|
static void
|
|
process_get (Snmp_Header *hdr, int intf)
|
|
{
|
|
Variable *var;
|
|
int idx;
|
|
int x;
|
|
int oidlen;
|
|
|
|
var = hdr->head;
|
|
while ( var ) {
|
|
|
|
/* Handle the 'GET PREFIX' request */
|
|
oidlen = Objids[SETPFX_OBJID].oid[0];
|
|
if (oid_ncmp(&var->oid, &Objids[SETPFX_OBJID], oidlen) == 0) {
|
|
var->var.ival = 2; /* assume not valid */
|
|
for(x = 0; x < 13; x++)
|
|
if (var->oid.oid[oidlen + x + 2] !=
|
|
addressEntry[intf].oid[x + 1])
|
|
break;
|
|
|
|
/* Address Match */
|
|
if (x == 13)
|
|
hdr->head->var.ival = 1;
|
|
var = var->next;
|
|
continue;
|
|
}
|
|
|
|
idx = find_var ( var );
|
|
switch ( idx ) {
|
|
case MADGE_OBJECT1:
|
|
/* reply with NO SUCH OBJECT */
|
|
var->type = ASN_NULL;
|
|
break;
|
|
case SYS_OBJID:
|
|
var->type = ASN_OBJID;
|
|
bcopy ( (caddr_t)&Objids[MY_OBJID],
|
|
(caddr_t)&var->var.oval,
|
|
sizeof(Objid) );
|
|
break;
|
|
case UPTIME_OBJID:
|
|
var->type = ASN_TIMESTAMP;
|
|
var->var.ival = get_ticks();
|
|
break;
|
|
case UNITYPE_OBJID:
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = UNITYPE_PRIVATE;
|
|
break;
|
|
case UNIVER_OBJID:
|
|
var->type = ASN_INTEGER;
|
|
switch ( Intf[intf].anp_sig_proto ) {
|
|
case ATM_SIG_UNI30:
|
|
var->var.ival = UNIVER_UNI30;
|
|
break;
|
|
case ATM_SIG_UNI31:
|
|
var->var.ival = UNIVER_UNI31;
|
|
break;
|
|
case ATM_SIG_UNI40:
|
|
var->var.ival = UNIVER_UNI40;
|
|
break;
|
|
default:
|
|
var->var.ival = UNIVER_UNKNOWN;
|
|
break;
|
|
}
|
|
break;
|
|
case DEVTYPE_OBJID:
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = DEVTYPE_USER;
|
|
break;
|
|
case MAXVCC_OBJID:
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = 1024;
|
|
break;
|
|
case PORT_OBJID:
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = intf + 1;
|
|
break;
|
|
case IPNM_OBJID:
|
|
var->type = ASN_IPADDR;
|
|
get_local_ip( ilmi_fd[intf], &var->var.aval );
|
|
break;
|
|
case ADDRESS_OBJID:
|
|
break;
|
|
case ATMF_PORTID:
|
|
var->type = ASN_INTEGER;
|
|
var->var.ival = 0x30 + intf;
|
|
break;
|
|
case ATMF_SYSID:
|
|
var->type = ASN_OCTET;
|
|
var->var.sval[0] = 6;
|
|
bcopy ( (caddr_t)&Cfg[intf].acp_macaddr,
|
|
(caddr_t)&var->var.sval[1], 6 );
|
|
break;
|
|
default:
|
|
/* NO_SUCH */
|
|
break;
|
|
}
|
|
var = var->next;
|
|
}
|
|
build_pdu ( hdr, PDU_TYPE_GETRESP );
|
|
send_resp ( intf, hdr, Resp_Buf );
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Find an OBJID from known ones
|
|
*
|
|
* in: Variable with valid OID
|
|
* out: OID number (index), -1 = not found
|
|
*/
|
|
static int
|
|
lmi_object_find (Variable *var)
|
|
{
|
|
Objid * obj_var;
|
|
Objid * obj_cur;
|
|
size_t x;
|
|
int y;
|
|
|
|
obj_var = &var->oid;
|
|
|
|
for (x = 0; x < NUM_OIDS; x++) {
|
|
obj_cur = &Objids[x];
|
|
for (y = 0; y < 128; y++) {
|
|
if (obj_var->oid[y] != obj_cur->oid[y])
|
|
break;
|
|
if (obj_var->oid[y] == 0) /* object ID endmark */
|
|
return (x);
|
|
}
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
#if 0
|
|
/******************************************************************************
|
|
*
|
|
* Append instance number to OID
|
|
*
|
|
* in: Variable, instance number
|
|
* out: zero = success
|
|
*
|
|
*/
|
|
static int
|
|
lmi_object_instance (Variable *var, int instnum)
|
|
{
|
|
int * oidptr;
|
|
int curlen;
|
|
|
|
oidptr = var->oid.oid;
|
|
curlen = oidptr[0]; /* current length */
|
|
if (curlen > 126)
|
|
return (1);
|
|
curlen++;
|
|
oidptr[curlen] = instnum;
|
|
oidptr[0] = curlen;
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Handle received GETNEXT
|
|
*
|
|
* in: Header with valid fields, interface number
|
|
* out: zero = success
|
|
*
|
|
*/
|
|
static int
|
|
lmi_rcvcmd_getnext (Snmp_Header *header, int intf)
|
|
{
|
|
int * oidptr;
|
|
int oidlen;
|
|
int oidnum;
|
|
int x;
|
|
|
|
oidnum = lmi_object_find(header->head);
|
|
oidptr = header->head->oid.oid;
|
|
oidlen = oidptr[0];
|
|
|
|
switch(oidnum) {
|
|
/* Should be because the remote side is attempting
|
|
* to verify that our table is empty
|
|
*/
|
|
case ADDRESS_OBJID:
|
|
if ( addressEntry[intf].oid[0] ) {
|
|
/* XXX - FIXME */
|
|
/* Our table is not empty - return address */
|
|
}
|
|
break;
|
|
|
|
/* Madge Collage sends GETNEXT for this */
|
|
case SETPFX_OBJID:
|
|
if(addressEntry[intf].oid[0]) { /* we have a prefix */
|
|
oidptr[0] += 14;
|
|
oidptr += oidlen; /* skip to last number */
|
|
oidptr++;
|
|
*oidptr++ = 13; /* length of prefix */
|
|
|
|
/* fill in the prefix */
|
|
for(x = 0; x < 13; x++) {
|
|
*oidptr++ = addressEntry[intf].oid[x+1];
|
|
}
|
|
header->head->type = ASN_INTEGER;
|
|
/* 1=valid, 2=invalid -- only 2 values */
|
|
header->head->var.ival = 1;
|
|
} else { /* no prefix available */
|
|
header->head->type = ASN_NULL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return (1); /* unknown object ID */
|
|
}
|
|
|
|
build_pdu(header, PDU_TYPE_GETRESP);
|
|
send_resp(intf, header, Resp_Buf);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Handle received TRAP
|
|
*
|
|
* in: Header with valid fields, interface number
|
|
* out: zero = success
|
|
*
|
|
*/
|
|
static int
|
|
lmi_rcvcmd_trap (Snmp_Header *header __unused, int intf)
|
|
{
|
|
|
|
bzero((caddr_t)&addressEntry[intf], sizeof(Objid));
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* ILMI State Processing Loop
|
|
*
|
|
*
|
|
*/
|
|
static void
|
|
ilmi_do_state(void)
|
|
{
|
|
struct timeval tvp;
|
|
fd_set rfd;
|
|
u_char buf[1024];
|
|
Variable *var;
|
|
int intf;
|
|
int maxfd = 0;
|
|
|
|
/*
|
|
* Loop forever
|
|
*/
|
|
for ( ; ; ) {
|
|
int count;
|
|
int n;
|
|
u_char *bpp;
|
|
Snmp_Header *Hdr;
|
|
|
|
/*
|
|
* SunOS CC doesn't allow automatic aggregate initialization.
|
|
* Initialize to zero which effects a poll operation.
|
|
*/
|
|
tvp.tv_sec = 15;
|
|
tvp.tv_usec = 0;
|
|
|
|
/*
|
|
* Clear fd_set and initialize to check this interface
|
|
*/
|
|
FD_ZERO ( &rfd );
|
|
for ( intf = 0; intf < MAX_UNITS; intf++ )
|
|
if ( ilmi_fd[intf] > 0 ) {
|
|
FD_SET ( ilmi_fd[intf], &rfd );
|
|
maxfd = MAX ( maxfd, ilmi_fd[intf] );
|
|
}
|
|
|
|
/*
|
|
* Check for new interfaces
|
|
*/
|
|
ilmi_open();
|
|
|
|
for ( intf = 0; intf < MAX_UNITS; intf++ ) {
|
|
/*
|
|
* Do any pre-message state processing
|
|
*/
|
|
switch ( ilmi_state[intf] ) {
|
|
case ILMI_COLDSTART:
|
|
/*
|
|
* Clear addressTable
|
|
*/
|
|
bzero ( (caddr_t)&addressEntry[intf], sizeof(Objid) );
|
|
|
|
/*
|
|
* Start by sending a COLD_START trap. This should cause the
|
|
* remote end to clear the associated prefix/address table(s).
|
|
*/
|
|
/* Build ColdStart TRAP header */
|
|
ColdStart_Header = build_cold_start();
|
|
build_pdu ( ColdStart_Header, PDU_TYPE_TRAP );
|
|
send_resp ( intf, ColdStart_Header, Resp_Buf );
|
|
|
|
/*
|
|
* Start a timeout so that if the next state fails, we re-enter
|
|
* ILMI_COLDSTART.
|
|
*/
|
|
/* atm_timeout() */
|
|
|
|
/* Enter new state */
|
|
ilmi_state[intf] = ILMI_INIT;
|
|
/* fall into ILMI_INIT */
|
|
|
|
case ILMI_INIT:
|
|
/*
|
|
* After a COLD_START, we need to check that the remote end has
|
|
* cleared any tables. Send a GET_NEXT request to check for this.
|
|
* In the event that the table is not empty, or that no reply is
|
|
* received, return to COLD_START state.
|
|
*/
|
|
PDU_Header = build_generic_header();
|
|
|
|
PDU_Header->head = (Variable *)malloc(sizeof(Variable));
|
|
if (PDU_Header->head == NULL) {
|
|
fprintf(stderr, "malloc() failed in %s()\n", __func__);
|
|
exit(1);
|
|
}
|
|
bzero(PDU_Header->head, sizeof(Variable));
|
|
|
|
var = PDU_Header->head;
|
|
bcopy ( (caddr_t)&Objids[ADDRESS_OBJID], (caddr_t)&var->oid,
|
|
sizeof(Objid) );
|
|
var->type = ASN_NULL;
|
|
var->next = NULL;
|
|
|
|
/*
|
|
* Send GETNEXT request looking for empty ATM Address Table
|
|
*/
|
|
PDU_Header->reqid = Req_ID++;
|
|
build_pdu ( PDU_Header, PDU_TYPE_GETNEXT );
|
|
send_resp ( intf, PDU_Header, Resp_Buf );
|
|
|
|
/*
|
|
* Start a timeout while looking for SET message. If we don't receive
|
|
* a SET, then go back to COLD_START state.
|
|
*/
|
|
/* atm_timeout() */
|
|
break;
|
|
|
|
case ILMI_RUNNING:
|
|
/* Normal SNMP processing */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
count = select ( maxfd + 1, &rfd, NULL, NULL, &tvp );
|
|
|
|
for ( intf = 0; intf < MAX_UNITS; intf++ ) {
|
|
/*
|
|
* Check for received messages
|
|
*/
|
|
if ( ilmi_fd[intf] > 0 && FD_ISSET ( ilmi_fd[intf], & rfd ) ) {
|
|
|
|
n = read ( ilmi_fd[intf], (caddr_t)&buf[1], sizeof(buf) - 1 );
|
|
if ( n == -1 && ( errno == ECONNRESET || errno == EBADF ) ) {
|
|
ilmi_state[intf] = ILMI_COLDSTART;
|
|
close ( ilmi_fd[intf] );
|
|
ilmi_fd[intf] = -1;
|
|
} else {
|
|
bpp = (caddr_t)&buf[1];
|
|
Hdr = asn_get_header(&bpp);
|
|
|
|
if ( Log && Debug_Level > 1 )
|
|
print_pdu(PDU_RECV, intf, Hdr, n, buf);
|
|
|
|
if (Hdr == NULL)
|
|
continue;
|
|
|
|
/* What we do with this messages depends upon the state we're in */
|
|
switch ( ilmi_state[intf] ) {
|
|
case ILMI_COLDSTART:
|
|
/* We should never be in this state here */
|
|
free_pdu ( Hdr );
|
|
break;
|
|
case ILMI_INIT:
|
|
/* The only messages we care about are GETNEXTs, GETRESPs, and TRAPs */
|
|
switch ( Hdr->pdutype ) {
|
|
case PDU_TYPE_GETNEXT:
|
|
lmi_rcvcmd_getnext(Hdr, intf);
|
|
break;
|
|
case PDU_TYPE_GETRESP:
|
|
/*
|
|
* This should be in response to our GETNEXT.
|
|
* Check the OIDs and go onto ILMI_RUNNING if
|
|
* the address table is empty. We can cheat and
|
|
* not check sequence numbers because we only send
|
|
* the one GETNEXT request and ILMI says we shouldn't
|
|
* have interleaved sessions.
|
|
*/
|
|
/*
|
|
* First look for empty table. If found, go to next state.
|
|
*/
|
|
if ((Hdr->error == SNMP_ERR_NOSUCHNAME) ||
|
|
((Hdr->error == SNMP_ERR_NOERROR) &&
|
|
( oid_ncmp ( &Objids[ADDRESS_OBJID], &Hdr->head->oid,
|
|
Objids[ADDRESS_OBJID].oid[0] ) == 1 ))) {
|
|
ilmi_state[intf] = ILMI_RUNNING; /* ILMI_REG; */
|
|
} else if (Hdr->error == SNMP_ERR_NOERROR) {
|
|
/*
|
|
* Check to see if this matches our address
|
|
* and if so, that it's a VALID entry.
|
|
*/
|
|
Atm_addr *aa;
|
|
int l;
|
|
int match = 1;
|
|
|
|
aa = &Intf[intf].anp_addr;
|
|
if ( aa->address_length == Hdr->head->oid.oid[13] ) {
|
|
for ( l = 0; l < aa->address_length; l++ ) {
|
|
if ( (int)((u_char *)(aa->address))[l] !=
|
|
Hdr->head->oid.oid[14 + l] ) {
|
|
match = 0;
|
|
}
|
|
}
|
|
}
|
|
if ( match ) {
|
|
if ( Hdr->head->var.ival == 1 ) {
|
|
ilmi_state[intf] = ILMI_RUNNING;
|
|
}
|
|
}
|
|
}
|
|
free_pdu ( Hdr );
|
|
break;
|
|
case PDU_TYPE_SET:
|
|
/* Look for SET_PREFIX Objid */
|
|
if ( oid_ncmp ( &Hdr->head->oid,
|
|
&Objids[SETPFX_OBJID],
|
|
Objids[SETPFX_OBJID].oid[0] ) == 0 ) {
|
|
set_prefix ( &Hdr->head->oid, Hdr, intf );
|
|
/* Reply to SET before sending our ADDRESS */
|
|
build_pdu(Hdr, PDU_TYPE_GETRESP);
|
|
send_resp( intf, Hdr, Resp_Buf );
|
|
set_address ( Hdr, intf );
|
|
} else {
|
|
build_pdu(Hdr, PDU_TYPE_GETRESP);
|
|
send_resp( intf, Hdr, Resp_Buf );
|
|
}
|
|
break;
|
|
case PDU_TYPE_TRAP:
|
|
/* Remote side wants us to start fresh */
|
|
lmi_rcvcmd_trap(Hdr, intf);
|
|
free_pdu ( Hdr );
|
|
break;
|
|
default:
|
|
/* Ignore */
|
|
free_pdu ( Hdr );
|
|
break;
|
|
}
|
|
break;
|
|
case ILMI_REG:
|
|
break;
|
|
case ILMI_RUNNING:
|
|
/* We'll take anything here */
|
|
switch ( Hdr->pdutype ) {
|
|
case PDU_TYPE_GET:
|
|
process_get ( Hdr, intf );
|
|
break;
|
|
case PDU_TYPE_GETRESP:
|
|
/* Ignore GETRESPs */
|
|
free_pdu ( Hdr );
|
|
break;
|
|
case PDU_TYPE_GETNEXT:
|
|
lmi_rcvcmd_getnext(Hdr, intf);
|
|
break;
|
|
case PDU_TYPE_SET:
|
|
/* Look for SET_PREFIX Objid */
|
|
if ( oid_ncmp ( &Hdr->head->oid,
|
|
&Objids[SETPFX_OBJID],
|
|
Objids[SETPFX_OBJID].oid[0] ) == 0 ) {
|
|
set_prefix ( &Hdr->head->oid, Hdr, intf );
|
|
/* Reply to SET before sending our ADDRESS */
|
|
build_pdu(Hdr, PDU_TYPE_GETRESP);
|
|
send_resp( intf, Hdr, Resp_Buf );
|
|
set_address ( Hdr, intf );
|
|
} else {
|
|
build_pdu(Hdr, PDU_TYPE_GETRESP);
|
|
send_resp( intf, Hdr, Resp_Buf );
|
|
}
|
|
break;
|
|
case PDU_TYPE_TRAP:
|
|
lmi_rcvcmd_trap(Hdr, intf);
|
|
free_pdu ( Hdr );
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
/* Unknown state */
|
|
free_pdu ( Hdr );
|
|
break;
|
|
}
|
|
} /* if n > 0 */
|
|
} /* if received message */
|
|
} /* for each interface */
|
|
} /* for ever loop */
|
|
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int c;
|
|
int i;
|
|
int Reset = 0; /* Should we send a coldStart and exit? */
|
|
|
|
/*
|
|
* What are we running as? (argv[0])
|
|
*/
|
|
progname = strdup ( (char *)basename ( argv[0] ) );
|
|
/*
|
|
* What host are we
|
|
*/
|
|
gethostname ( hostname, sizeof ( hostname ) );
|
|
|
|
/*
|
|
* Ilmid needs to run as root to set prefix
|
|
*/
|
|
if ( getuid() != 0 ) {
|
|
fprintf ( stderr, "%s: needs to run as root.\n", progname );
|
|
exit ( -1 );
|
|
}
|
|
|
|
/*
|
|
* Parse arguments
|
|
*/
|
|
while ( ( c = getopt ( argc, argv, "d:fr" ) ) != -1 )
|
|
switch ( c ) {
|
|
case 'd':
|
|
Debug_Level = atoi ( optarg );
|
|
break;
|
|
case 'f':
|
|
foregnd++;
|
|
break;
|
|
case 'r':
|
|
Reset++;
|
|
break;
|
|
case '?':
|
|
fprintf ( stderr, "usage: %s [-d level] [-f] [-r]\n",
|
|
progname );
|
|
exit ( -1 );
|
|
/* NOTREACHED */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If we're not doing debugging, run in the background
|
|
*/
|
|
if ( foregnd == 0 ) {
|
|
if ( daemon ( 0, 0 ) )
|
|
err ( 1, "Can't fork" );
|
|
} /* else
|
|
setbuf ( stdout, NULL ); */
|
|
|
|
signal ( SIGUSR1, Increment_DL );
|
|
signal ( SIGUSR2, Decrement_DL );
|
|
|
|
/*
|
|
* Open log file
|
|
*/
|
|
if ( Debug_Level ) {
|
|
if ( foregnd ) {
|
|
Log = stderr;
|
|
} else {
|
|
if ( ( Log = fopen ( LOG_FILE, "a" ) ) == NULL )
|
|
Log = NULL;
|
|
}
|
|
}
|
|
if ( Log )
|
|
setbuf ( Log, NULL );
|
|
|
|
/*
|
|
* Get our startup time
|
|
*/
|
|
(void) gettimeofday ( &starttime, NULL );
|
|
starttime.tv_sec--;
|
|
starttime.tv_usec += 1000000;
|
|
|
|
/* Randomize starting request ID */
|
|
Req_ID = starttime.tv_sec;
|
|
|
|
/*
|
|
* Reset all the interface descriptors
|
|
*/
|
|
for ( i = 0; i < MAX_UNITS; i++ ) {
|
|
ilmi_fd[i] = -1;
|
|
}
|
|
/*
|
|
* Try to open all the interfaces
|
|
*/
|
|
ilmi_open ();
|
|
|
|
/*
|
|
* If we're just sending a coldStart end exiting...
|
|
*/
|
|
if ( Reset ) {
|
|
for ( i = 0; i < MAX_UNITS; i++ )
|
|
if ( ilmi_fd[i] >= 0 ) {
|
|
/* Build ColdStart TRAP header */
|
|
ColdStart_Header = build_cold_start();
|
|
build_pdu ( ColdStart_Header, PDU_TYPE_TRAP );
|
|
send_resp ( i, ColdStart_Header, Resp_Buf );
|
|
if ( Debug_Level > 1 && Log ) {
|
|
write_timestamp();
|
|
fprintf ( Log, "Close ilmi_fd[%d]: %d\n",
|
|
i, ilmi_fd[i] );
|
|
}
|
|
close ( ilmi_fd[i] );
|
|
}
|
|
exit ( 2 );
|
|
}
|
|
|
|
ilmi_do_state();
|
|
|
|
exit(0);
|
|
}
|