You can write a program to get network management information from TCPIP by using the Network Management Interface API interface. It may be just as easy to use the IPSEC -f display command, and post process the output.
The Network Management Interface (nmi) is documented here (and links from this page) but it is not very easy to follow and get working. Some of the facts are there, but a lot is left to poor end user to determine how it works.
Overall the program is pretty simple
- Connect to the server
- Receive the init flow (the header) from the server
- Send a request to the server
- Receive the data from the server – this is where it gets a little tricker.
The make file
It took me a while to get my program to compile. Here is the make file I used
# Our first Makefile
cparmsa= -Wc,"SSCOM,DEBUG,DEF(MVS,_ALL_SOURCE),UNDEF(_OPEN_DEFAULT,_NO_PROTO)
cparmsb= ,SO,SHOW,LIST(client.lst),XREF,ILP32,DLL,SKIPS(HIDE),LANGLVL(EXTENDED)"
syslib= -I'/usr/include' -I'/usr/include/sys'
all: main
parts = client.o
main: $(parts)
cc -o client $(parts)
%.o: %.c
cc -c -o $@ $(syslib) $(cparmsa)$(cparmsb) -V $<
clean:
rm *.o
It took time to find I needed UNDEF(_OPEN_DEFAULT,_NO_PROTO) . _NO_PROTO prevents function prototypes from being generated. As I wanted function prototypes, I had to UNDEF it!
My C program is was in file client.c
Include files
#define _OPEN_SYS_SOCK_IPV6
#define _XOPEN_SOURCE_EXTENDED 1
#define _OPEN_SYS_SOCK_EXT
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <ezbnmsec.h>
#include <sys/un.h>
#include <stdarg.h>
#include <iconv.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <//'COLIN.C.SOURCE(PRINTHEX)'>
Connect to the server
This is a standard TCP/IP send/receive application using AF_UNIX and a socket stream.
int sd=-1, rc;
struct sockaddr_un serveraddr;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
#define SERVER_PATH "/var/sock/ipsecmgmt"
strcpy(serveraddr.sun_path, SERVER_PATH);
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
if (rc < 0)
{
perror("connect() failed");
break;
}
My nmi request structure
I used
struct {
struct _NMsecMessageHdr header;
struct _NMsecRecordHdr record;
struct _NMsecSecDesc section;
struct _NMsecInFilter filter ;
} nmi;
I could use this to send a request to IKED, to receive data from the response to the initial connect.
Get the initial data from the server
After the connect request, the server sends back an init data stream into the nmi structure.
if (recv(sd,&nmi.header, sizeof(nmi.header), 0) < 0)
{
tcperror("Recv()");
exit(6);
}
printf("Return code:%d\n",nmi.header.NMsMRc);
if (nmi.header.NMsMRc != 0)
{
printf("Reason code:%d\n",nmi.header.NMsMRsn);
printf("Message type:%hd\n",nmi.header.NMsMType);
printf("NMI version :%hd\n",nmi.header.NMsMVersion);
}
printf("==============\n");
Setting up input structure
I initialised the nmi structure using
memset(&nmi.filter,0,sizeof(nmi.filter));
nmi.header.NMsMIdent = NMsec_MSGIDENT ;
nmi.header.NMsMHdrLength = sizeof(nmi.header);
nmi.header.NMsMVersion = NMsec_VERSION2;
nmi.header.NMsMMsgLength = sizeof(nmi);
// output record descriptor
memset(&nmi.header.NMsMOutRec ,0,sizeof(nmi.header.NMsMOutRec));
// input record descriptor
nmi.header.NMsMInRec.NMsIROffset = offsetof(nmi,record) ;
nmi.header.NMsMInRec.NMsIRRsvd1 = 0;
nmi.header.NMsMInRec.NMsIRNumber = 0; // start with none
// need tcpip address space padded with blanks
memset(&nmi.header.NMsMTarget,' ',sizeof(nmi.header.NMsMTarget));
memcpy(&nmi.header.NMsMTarget,"TCPIP",5);
// clear the record
memset(&nmi.record,0,sizeof(nmi.record));
// record definition
nmi.record.NMsRIdent = NMsec_RECIDENT;
nmi.record.NMsRLength= sizeof(nmi.record) + sizeof(nmi.section) + sizeof(nmi.spare);
nmi.record.NMsRNumCascadeSecDesc = 0;
nmi.record.NMsRNumSecDesc = 1; // 1 section
// section
nmi.section.NMsSOffset = sizeof(nmi.spare ); // offset from start of record
nmi.section.NMsSLength = sizeof(nmi.spare);
nmi.section.NMsSNumber = 0;
//
// specify the request
//
// nmi.header.NMsMType = NMsec_GET_IPFLTCURR;
// nmi.header.NMsMType = NMsec_GET_SUMMARY;
// nmi.header.NMsMType = NMsec_GET_STACKINFO;
// nmi.header.NMsMType = NMsec_GET_IPFLTDEFAULT ;
// nmi.header.NMsMType = NMsec_GET_IPFLTCURR ;
// nmi.header.NMsMType = NMsec_GET_IPFLTPOLICY ;
Build the request
We were passed in the request type ( option 1 to 6) and an optional filter name in pFilterName;
If we have a filter name, we need to set the number of headers, number of sections, and number of filters – they were all set to 0.
// process the input parameters
int type = NMsec_GET_SUMMARY ;// preset this
char * pFilterValue = ""; // no filter
if (argc >= 2 ) type = atoi(argv[1]);
if (argc >= 3 ) pFilterValue = argv[2];
nmi.header.NMsMType = type;
// we can have an optional filter for these requests
if ((nmi.header.NMsMType == NMsec_GET_IPFLTCURR
||nmi.header.NMsMType == NMsec_GET_IPFLTDEFAULT
||nmi.header.NMsMType == NMsec_GET_IPFLTDEFAULT )
&& strlen(pFilterValue) > 0
)
{
if ( strlen(pFilterValue) > sizeof(nmi.filter.NMsFltObjName))
printf("Filter name %s is too long \n",pFilterValue);
else
{
// set up the filter
memset(&nmi.filter.NMsFltObjName,' ',sizeof(nmi.filter.NMsFltObjName));
memcpy(&nmi.filter.NMsFltObjName,pFilterValue,strlen(pFilterValue));
nmi.header.NMsMInRec.NMsIRNumber = 1; // one header record
nmi.record.NMsRNumSecDesc = 1; // 1 section
nmi.filter.NMsFltFlagObjNam = 1; // one filter
}
}
and send it
if (send(sd,&nmi , sizeof(nmi ), 0) < 0)
{
printf("send failed\n");
tcperror("Send()");
exit(5);
}
printf("send done\n");
Receiving the response
This gets a little more complex. You cannot send an nmi structure larger than about 10KB, but the data returned can be much larger than this!
You need to peek at the data in the buffer, and allocate a buffer big enough for the returned data, then receive the data
Initial set up for receive
struct inData {
struct _NMsecMessageHdr header;
struct _NMsecRecordHdr record;
struct _NMsecSecDesc section;
char spare[1024 ];
} ;
struct inData * pInData;
char * pInBuffer;
int lInBuffer =1024;
pInBuffer = malloc(lInBuffer);
if (pInBuffer == 0)
{
printf("Malloc failed\n");
exit(12);
}
pInData = (struct inData * ) pInBuffer;
Use the MSG_PEEK option of recv.
int lRecv = 0;
lRecv = recv(sd,pInBuffer,lInBuffer, MSG_PEEK);
if (lRecv < 0) // there was a problem
{
printf("recv failed\n");
tcperror("Recv()");
exit(6);
}
// look at the length of the data from the peek buffer
// if it is larger than our buffer reallocate buffer
if (pInData -> header. NMsMMsgLength > lInBuffer)
{
printf("Buffer not big enough - making it %d\n",pInData -> header. NMsMMsgLength);
lInBuffer = pInData -> header. NMsMMsgLength; // use the received length
free(pInBuffer);
pInBuffer = malloc(lInBuffer);
if (pInBuffer == 0)
{
printf("Malloc failed\n");
exit(12);
}
pInData = (struct inData * ) pInBuffer;
}
and now the real receive
// now do the real receive
lRecv = recv(sd,pInBuffer,lInBuffer , 0 );
if (lRecv < 0)
{
printf("rec failed\n");
tcperror("Recv()");
exit(6);
}
printf("Length received %d\n",lRecv);
// set up defensive programming
char * pEnd;
pEnd = pInBuffer + lRecv ;
Check it worked
printf("Return code:%d\n",pInData->header.NMsMRc);
printf("Reason code:%d\n",pInData->header.NMsMRsn);
printf("Message type:%hd\n",pInData->header.NMsMType);
if (InData->header.NMsMRc > 0)
{
// error handling
}
Process the data
printf("Output total %d actual %d\n",pInData->header.NMsMOutRec.NMsORTotal
, pInData->header.NMsMOutRec.NMsORNumber);
char * pCurr; // current position in the output buffer.
struct _NMsecRecordHdr * pRecord ;
struct _NMsecSecDesc * pSection;
int nRecords = pInData->header.NMsMOutRec.NMsORNumber;
printf("nRecords %d\n",nRecords);
// output record descriptor locates the first output record
pRecord = (struct _NMsecRecordHdr *)
(pInBuffer + pInData->header.NMsMOutRec.NMsOROffset);
for (int rLoop = 0;rLoop <nRecords;rLoop ++)
{
pCurr = (char *) pRecord;
// defensive programming
if ( pCurr > pEnd)
{
printf(" Record ran off the end of the buffer \n");
leave = 1;
break;
}
printf("Record number %d\n",rLoop);
// section follows the header
pCurr += sizeof(pInData->record); // size of header
pSection = (struct _NMsecSecDesc * ) pCurr;
// for number of sections defined in the record header
for (unsigned short sLoop = 0; sLoop < pRecord -> NMsRNumSecDesc ; sLoop ++)
{
char * pData = (char *) pRecord + pSection-> NMsSOffset;
int lData = pSection-> NMsSLength;
// each section can have multiple data items
for (int sdLoop = 0; sdLoop < pSection-> NMsSNumber; sdLoop ++)
{
.... process the data
pData += lData;
} // for (int sdLoop = 0; sdLoop < pSection-> NMsSNumber; sdLoop ++)
pSection ++; // move to the next section
} // for (unsigned short sLoop = 0; sLoop < pRecord -> NMsRNumSecDesc ; sLoop ++)
// move to the next record by jumping the length of the record
pRecord = (struct _NMsecRecordHdr *)((char *) pRecord + pRecord -> NMsRLength);
} // for (int rLoop = 0;rLoop <nRecords;rLoop ++)
Process the data
printf("Record:%d Section:%d part %d \n",rLoop,sLoop,sdLoop);
if( pInData->header.NMsMType == NMsec_GET_IPFLTCURR )
{
printFilter(pData);
// printHex(stdout,pData,lData);
}
else
if( pInData->header.NMsMType == NMsec_GET_IPFLTDEFAULT) printFilter(pData);
else
if( pInData->header.NMsMType == NMsec_GET_IPFLTPOLICY ) printFilter(pData);
else
if( pInData->header.NMsMType == NMsec_GET_IPFLTCURR ) printFilter(pData);
else
if( pInData->header.NMsMType == NMsec_GET_SUMMARY ) printStatistics(pData);
else
if( pInData->header.NMsMType == NMsec_GET_STACKINFO ) printStackInfo(pData);
else
printHex(stdout,pData,lData);
Display the data
Process the data for example
void printStackInfo(char * pData)
{
struct _NMsecStack * pStack = (struct _NMsecStack *) pData ;
printf(" Stack name %24.24s\n",pStack -> NMsStackName);
printf(" Configured filters %n",pStack -> NMsStackFilterCount ) ;
printf(" Defensive filters %d\n",pStack -> NMsStackDefFltCount ) ;
}
In
void printStatistics(char * pData)
{
struct _NMsecStatistics * pStats = (struct _NMsecStatistics *) pData ;
printf(" filter packets denied %lld\n",pStats -> NMsStatFilterDeny);
printf(" filter packets discarded mismatch %lld\n",pStats -> NMsStatFilterMismatch) ;
printf(" filter packets discarded match %lld\n",pStats -> NMsStatFilterMatch ) ;
}
You need to use %lld – as the numbers are 64 bit integers.
These numbers are reset if the rule is altered.