Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / blaze / netsim.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: netsim.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
//
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
//
// The above named 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 work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
// ========== Copyright Header End ============================================
/* icq:
* SNOOP
*
* "netsim.cc", low level communications between blaze and switchsim, syncsim.
*
* There are two major service types:
*
* Global-Time-Sync packets (simple C strings)
* Network packets (network-packet plus 8-byte switchsim header
* containing length and timing info)
*
* There are three major implementations:
*
* Socket-based,
* Mmap-based,
* Snoop-based.
*
*
* Someday the UI for disconnect/reconnect will be moved here too, no
* need to duplicate that in every net-device-sim...
*
* netsim_connect() name parsing:
*
* "sync:host/port"
* "switch:host/port"
*
* currently we only support socket-based, and it is not yet part of the
* name parsing...
*/
/* --->> NOTICE THERE ARE NO BLAZE INCLUDES HERE, KEEP IT THAT WAY !!! <<--- */
/* standard C includes */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <strings.h>
#include <assert.h>
#include <stdarg.h>
/* unix/Solaris includes */
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <rpc/xdr.h>
#include <synch.h>
#include <fcntl.h>
#include <netdb.h>
#include <stropts.h> /* strbuf, getmsg, putmsg */
#include <sys/dlpi.h> /* dlbindack, dlokack, etc..., DL_ defines... */
/* this file's public include */
#include "netsim.h"
/* private includes */
#include "eth.h" /* general purpose ethernet packet decoder */
/* bogus legacy globals ---------------------------------------- */
int netsim_switch_flag = 1;
char netsim_switch_server_name[MAXPATHLEN] = {'\0'};
char netsim_switch_ipc_type[MAXPATHLEN] = {'\0'};
char netsim_mmap_dir[MAXPATHLEN] = {'\0'};
int netsim_ipc_type = NETSIM_IPC_SOCKET;
/* configuration for this file */
#define SIMGE_SWITCH_VERSION "1.0" /* expecting switchsim to support */
#define USE_POLL 1 /* cant see why this is used, PAL*/
int netsim_debug = 0; /* debug level */
/* swtchdr.pkt_type values -------------------------------------- */
#define PKT_DATA 0
#define PKT_CTRL_VERSION 1
#define PKT_CTRL_IPC_TYPE 2
#define PKT_CTRL_KEY_TYPE 3
#define PKT_CTRL_INIT_END 4
#define PKT_CTRL_ACK_BASE 100
#define PKT_CTRL_VERSION_OK (PKT_CTRL_ACK_BASE + PKT_CTRL_VERSION)
#define PKT_CTRL_IPC_TYPE_OK (PKT_CTRL_ACK_BASE + PKT_CTRL_IPC_TYPE)
#define PKT_CTRL_KEY_TYPE_OK (PKT_CTRL_ACK_BASE + PKT_CTRL_KEY_TYPE)
#define PKT_CTRL_INIT_END_OK (PKT_CTRL_ACK_BASE + PKT_CTRL_INIT_END)
#define PKT_CTRL_ERROR 400
#define PKT_CTRL_IPC_SOCKET 0
#define PKT_CTRL_IPC_MMAP 1
#define PKT_CTRL_KEY_ETH 0
#define PKT_CTRL_KEY_IP 1
/*
* forward decls -----------------------------------------------------
*/
void vargs_error(int thresh, const char *fmt, ...);
static int
switch_negotiate(int fd, int ipc_type, int key_type, char *mmap_idstr, int mmap_file_size);
static int
ipc_socket_read_pkt(int fd, char *pkt, int maxlen, swtchdr *hdr, int timout);
static int
ipc_mmap_read_pkt(int fd, char *pkt, int maxlen, swtchdr *hdr, int timout);
static int
ipc_socket_write_pkt(int fd, char *pkt, int len, swtchdr *hdr);
static int
ipc_mmap_write_pkt(int fd, char *pkt, int pktlen, swtchdr *hdr);
/*
* -------- global variables --------------------------------------------------
*/
enum { _NONE=0, _SWITCH, _SYNC }; // service types
enum { _CLOSED=0, _SOCKET, _MMAP, _SNOOP }; // service implementations
static struct {
int service; /* _SWITCH, _SYNC */
int implt; /* _SOCKET, _MMAP, _SNOOP */
} info[ FD_SETSIZE ]; /* indexed by `fd' of open socket/mmap/snoop file */
////////////////////////////////////////////////////////////////////////////////
// //
// These are the four public interface functions connect, get, put, close //
// //
////////////////////////////////////////////////////////////////////////////////
/* ------------------------------ simplified ----------------------------------
* ...currently only handles socket, not mmap, not snoop...
*
*/
int netsim_connect(const char *switchandport)
{
int ipc_type = PKT_CTRL_IPC_SOCKET; /* vs mmap */
int key_type = 0; /* ip vs mac routing in switchsim */
const char * p, * q;
int i, portno, service;
char host[ MAXPATHLEN ];
struct hostent *server;
struct sockaddr_in serv_addr;
int sockfd;
p = switchandport;
if (strncmp (switchandport, "sync:", 5) == 0) { // "sync:"
service = _SYNC;
p += 5;
} else if (strncmp (switchandport, "switch:", 7) == 0) { // "switch:"
service = _SWITCH;
p += 7;
} else {
service = _SWITCH;
}
if ((q = strrchr (p, '/')) != NULL) { // "host/port"
i = q - p;
strncpy (host, p, MAXPATHLEN);
host[ i ] = '\0';
portno = atoi (&host[ i+1 ]);
} else {
strncpy (host, p, MAXPATHLEN);
if (service == _SYNC)
portno = SYNC_DEFAULT_PORT;
else
portno = SWITCH_DEFAULT_PORT;
}
if (netsim_debug) fprintf(stderr,"netsim_connect %s: \"%s\", %d\n",
(service == _SYNC ? "sync" : "switch"), host, portno);
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*--SOCKET--*/
if (sockfd < 0) {
fprintf(stderr, "ERROR: tryconnect: socket: %s \n", strerror(errno));
return -1;
}
server = gethostbyname(host); /*--GETHOSTBYNAME--*/
if (server == NULL) {
fprintf(stderr, "ERROR: tryconnect: gethostbyname: %s \n", strerror(h_errno));
close(sockfd);
return -1;
}
bzero((char *) &serv_addr, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno); /*--CONNECT--*/
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
fprintf(stderr, "ERROR: tryconnect: connect: %s \n", strerror(errno));
close(sockfd);
return -1;
}
if (netsim_debug) fprintf(stderr,"netsim_connect fd = %d\n", sockfd);
if (service == _SYNC) {
info[ sockfd ].service = _SYNC;
} else /*service == _SWITCH*/ { /*--negotiate--*/
if (switch_negotiate(sockfd, ipc_type, key_type, NULL, 0) < 0) {
fprintf(stderr, "ERROR: tryconnect: negotiate: with %s failed. \n",
host);
close(sockfd);
return -1;
}
info[ sockfd ].service = _SWITCH;
}
info[ sockfd ].implt = _SOCKET;
return sockfd;
}
/* ------------------------------ simplified ----------------------------------
*/
int netsim_close (int fd)
{
close (fd);
info[ fd ].service = _NONE;
info[ fd ].implt = _CLOSED;
return 0;
}
/* ------------------------------ simplified ----------------------------------
* result < 0 error
* result = 0 connection closed, or errno = EINTR (from SIGUSR1)
* result > 0 success, --> actual bytes read is returned in hdr->pkt_len <--
*/
int
netsim_getmsg(int fd, char * buf, int maxlen, swtchdr * hdr)
{
int result;
if (info[ fd ].service == _SYNC) {
if (info[fd].implt == _SOCKET) {
/* ? read, ? fread, ? fgets, ? getmsg, ? ... */
int flags = 0;
struct strbuf ctrlbuf;
struct strbuf databuf;
databuf.maxlen = maxlen;
databuf.buf = buf;
result = getmsg (fd, NULL/*ctrlbuf*/, &databuf, NULL/*flagsp*/);
/* man says 0 => full message copied,
+ => partial message copied,
- => error
but does not say how to tell if connection closed remotely ??? */
if (result >= 0)
result = databuf.len; /* actual num bytes copied */
else if (errno == EINTR)
result = 0; /* our convention */
else {
perror ("netsim: get sync msg: ");
result = -1;
}
} else
result = -1;
} else /* _SWITCH */ {
if (info[fd].implt == _SOCKET) {
result = ipc_socket_read_pkt(fd, buf, maxlen, hdr, -1);
} else if (info[fd].implt == _MMAP) {
result = ipc_mmap_read_pkt(fd, buf, maxlen, NULL, -1);
} else
result = -1;
}
return result;
}
/* ------------------------------ simplified ----------------------------------
* result < 0 error, including connection closed, errno = EINTR (SIGUSR1) ???
* result = 0 ???
* result > 0 success, result bytes written
*/
int
netsim_putmsg(int fd, char * buf, int len, swtchdr * hdr)
{
int result;
errno = 0;
if (info[fd].service == _SYNC) {
if (info[fd].implt == _SOCKET) {
/* ? write, ? fwrite, ? fputs, ? putmsg, ? ... */
int flags = 0;
struct strbuf ctrlbuf;
struct strbuf databuf;
databuf.buf = buf;
databuf.len = len;
result = putmsg (fd, NULL/*ctrlbuf*/, &databuf, NULL/*flagsp*/);
/* man says 0 => full message copied,
- => error
but does not say how to tell if connection closed remotely ??? */
if (result >= 0)
result = databuf.len; /* actual num bytes copied */
else if (errno == EINTR)
result = -1; /* our (confused!) convention */
else {
perror ("netsim: put sync msg: ");
result = -1;
}
} else
result = -1;
} else /* _SWITCH */ {
if (info[fd].implt == _SOCKET) {
result = ipc_socket_write_pkt(fd, buf, len, hdr);
} else if (info[fd].implt == _MMAP) {
result = ipc_mmap_write_pkt(fd, buf, len, hdr);
} else if (info[fd].implt == _SNOOP){
struct strbuf data;
data.buf = (char *) buf;
data.maxlen = MAXDLBUF;
data.len = len;
result = putmsg(fd, NULL, &data, 0);
if (result == 0) result = sizeof(hdr) + len;
} else
result = -1;
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
// //
// The following section is for Socket-based //
// ^^^^^^ //
////////////////////////////////////////////////////////////////////////////////
#define ETHER_ADDR_LEN 6
#define ETHER_HDR_LEN (6 + 6 + 2)
#define SWITCH_PKT_HDR_SIZE 8
#define SWITCH_ETHERMTU (ETHERMTU + ETHER_HDR_LEN)
#define MAX_SWITCH_PKT_SIZE (SWITCH_ETHERMTU + SWITCH_PKT_HDR_SIZE + sizeof(ushort_t))
/*note that timout==-1 means wait forever, which is the only value used...
*change: returns 0 for socket closed at other end, PAL.
*/
static int
read_all(int fd, char *buf, int len, int timout)
{
int result, saved_len;
struct pollfd pfd;
int index = 0;
if (len == 0)
return 0;
saved_len = len;
while (len > 0) {
#if USE_POLL
/* I can't see any reason for using poll here, either we wait in poll
* or we wait in read, what's the difference...???...
* its just going to cost more syscalls...
*/
pfd.fd = fd;
pfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
result = poll(&pfd, 1, timout);
/*
* poll results:
* 0 => timeout (can't happen with -1 as the timeout value passed in)
* >0 => number of `active' files in polling list
* <0 => chech errno for {EAGAIN, EFAULT, EINTR, EINVAL}
*
* what the rest of simge wants:
* 0 => socket closed (either from other end, indicated by zero-length
* packet received, or from this end by the UI,
* indicated by EINTR (SIGUSR1).
* <0 => other error
* >0 => that many bytes read
*/
if (result <= 0) {
if (result < 0 && (errno == EINTR)) {
return 0; /* this is the SIGUSR1 from our UI case, this thread
* is being interrupted because the UI is closing
* the socket from this end */
} else if (result == 0) {
fprintf(stderr, "ERROR: read_all: poll(fd=%d) timedout \n", fd);
return -1;
} else {
fprintf(stderr, "ERROR: read_all: poll: %s \n", strerror(errno));
return -1;
}
}
assert(result == 1);
#endif /* USE_POLL */
result = read(fd, &buf[index], len);
if (result == len) {
return saved_len;
}
if (result > 0) {
index += result;
len -= result;
} else if (result < 0) {
fprintf(stderr, "ERROR: read_all: read: %s \n", strerror(errno));
return -1;
} else if (result == 0) {
fprintf(stderr, "INFO: read_all: read: connection closed on the socket. \n");
return 0; /*
* this is the "socket closed from the other end" case
*/
}
}
/* NOT REACHED */
return -1;
}
static int
write_all(int fd, char *buf, int len)
{
int result;
int index = 0;
int saved_len = len;
while (len > 0) {
result = write(fd, &buf[index], len);
if (result == len)
return saved_len;
if (result > 0) {
index += result;
len -= result;
} else if (result < 0) {
fprintf(stderr, "ERROR: write_all: write: %s \n", strerror(errno));
return -1;
} else if (result == 0) {
fprintf(stderr, "ERROR: write_all: write: returned 0 instead of writing %d bytes \n", len);
return -1;
}
}
/* NOT REACHED */
return -1;
}
/*
* now returns (buf_len *PLUS* hdr_len) !!! PAL.
* so that 0 can be the unambiguous indicator of socket closed at other end.
*
* on successful read store actual length read into hdr->pkt_len. PAL.
*/
static int
ipc_socket_read_pkt(int fd, char *pkt, int maxlen, swtchdr *hdr, int timout)
{
int pktlen, result;
swtchdr local_hdr, *hptr;
if (hdr == NULL)
hptr = &local_hdr;
else
hptr = hdr;
result = read_all(fd, (char *) hptr, sizeof(swtchdr), timout);
if (result <= 0) { /* was < 0), PAL*/
return result;
}
assert(result == sizeof(swtchdr));
pktlen = ntohl(hptr->pkt_len); /* real eth-packet len from switchsim hdr */
if (pktlen > maxlen) {
fprintf(stderr, "ERROR: ipc_socket_read_pkt: pktlen %d > maxlen %d \n",
pktlen, maxlen);
return -1;
}
if (pktlen == 0) {
hptr->pkt_len = pktlen; /*PAL*/
return pktlen + sizeof(swtchdr); /*was: pktlen; PAL*/
}
if ((result = read_all(fd, pkt, pktlen, timout)) <= 0) { /*was: <0, PAL*/
return result;
}
assert(result == pktlen);
hptr->pkt_len = pktlen; /*PAL*/
return pktlen + sizeof(swtchdr); /*was: pktlen; PAL*/
}
/*
* result = result-from-writev-syscall:
* <0 error
* =0 ?
* >0 success, number of bytes written
*/
static int
ipc_socket_write_pkt(int fd, char *pkt, int len, swtchdr *hdr)
{
int result;
swtchdr *hptr, local_hdr;
struct iovec wiovec[2];
if (hdr == NULL) {
hptr = &local_hdr;
hptr->pkt_type = htonl(PKT_DATA);
} else {
hptr = hdr;
}
hptr->pkt_len = htonl(len);
wiovec[0].iov_base = (char *) hptr;
wiovec[0].iov_len = sizeof(swtchdr);
if (len == 0) {
result = writev(fd, wiovec, 1);
return result;
} else {
wiovec[1].iov_base = pkt;
wiovec[1].iov_len = len;
result = writev(fd, wiovec, 2);
return result;
}
/* NOT REACHED */
return -1;
}
/* more swtchdr stuff -------------------------------------- */
typedef struct pkt_ctrl_ipc_type {
int ipc_type;
char *mmap_idstr;
int mmap_file_size;
} pkt_ctrl_ipc_type_t;
typedef struct pkt_ctrl_key_type {
int key_type;
} pkt_ctrl_key_type_t;
static int
process_ack(int fd, int type)
{
int result;
swtchdr hdr;
char pkt[SWITCH_ETHERMTU];
result = ipc_socket_read_pkt(fd, pkt, SWITCH_ETHERMTU, &hdr, TIMEOUT_FIVE_SECOND);
if (result <= 0) { /* was < 0, PAL*/
return -1;
}
switch (hdr.pkt_type) {
case PKT_CTRL_VERSION_OK:
case PKT_CTRL_IPC_TYPE_OK:
case PKT_CTRL_KEY_TYPE_OK:
case PKT_CTRL_INIT_END_OK:
if (type == hdr.pkt_type)
result = 0;
else
result = -1;
break;
case PKT_CTRL_ERROR:
result = -1;
break;
default:
result = -1;
break;
}
return result;
}
static int
process_version(int fd)
{
XDR xdr;
char *ptr;
union {
char pkt[SWITCH_ETHERMTU];
uint64_t force_alignment;
} aligned_pkt;
swtchdr hdr;
int len, result;
ptr = (char *) SIMGE_SWITCH_VERSION;
xdrmem_create(&xdr, aligned_pkt.pkt, SWITCH_ETHERMTU, XDR_ENCODE);
result = xdr_string(&xdr, &ptr, 64);
if (!result) {
xdr_destroy(&xdr);
return -1;
}
len = xdr_getpos(&xdr);
xdr_destroy(&xdr);
hdr.pkt_type = htonl(PKT_CTRL_VERSION);
hdr.pkt_len = htonl(len);
result = ipc_socket_write_pkt(fd, aligned_pkt.pkt, len, &hdr);
if (result < 0)
return result;
result = process_ack(fd, PKT_CTRL_VERSION_OK);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: version %s not accepted by switch. \n", ptr);
return -1;
}
return 0;
}
static int
process_ipc_type(int fd, int ipc_type, char *mmap_idstr, int mmap_file_size)
{
int len;
swtchdr hdr;
XDR xdr;
union {
char pkt[SWITCH_ETHERMTU];
uint64_t force_alignment;
} aligned_pkt;
pkt_ctrl_ipc_type_t data;
int result = 0;
data.ipc_type = ipc_type;
if (mmap_idstr == NULL)
data.mmap_idstr = (char *) "";
else
data.mmap_idstr = mmap_idstr;
data.mmap_file_size = mmap_file_size;
xdrmem_create(&xdr, aligned_pkt.pkt, SWITCH_ETHERMTU, XDR_ENCODE);
result = xdr_int(&xdr, &data.ipc_type);
if (!result) {
xdr_destroy(&xdr);
return -1;
}
result = xdr_string(&xdr, &data.mmap_idstr, MAXPATHLEN);
if (!result) {
xdr_destroy(&xdr);
return -1;
}
result = xdr_int(&xdr, &data.mmap_file_size);
if (!result) {
xdr_destroy(&xdr);
return -1;
}
len = xdr_getpos(&xdr);
xdr_destroy(&xdr);
hdr.pkt_type = htonl(PKT_CTRL_IPC_TYPE);
hdr.pkt_len = htonl(len);
result = ipc_socket_write_pkt(fd, aligned_pkt.pkt, len, &hdr);
if (result < 0)
return result;
result = process_ack(fd, PKT_CTRL_IPC_TYPE_OK);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: ipc_type %d not accepted by switch. \n", ipc_type);
}
return 0;
}
static int
process_key_type(int fd, int key_type)
{
int len;
swtchdr hdr;
XDR xdr;
union {
char pkt[SWITCH_ETHERMTU];
uint64_t force_alignment;
} aligned_pkt;
pkt_ctrl_key_type_t data;
int result = 0;
data.key_type = key_type;
xdrmem_create(&xdr, aligned_pkt.pkt, SWITCH_ETHERMTU, XDR_ENCODE);
result = xdr_int(&xdr, &data.key_type);
if (!result) {
xdr_destroy(&xdr);
return -1;
}
len = xdr_getpos(&xdr);
xdr_destroy(&xdr);
hdr.pkt_type = htonl(PKT_CTRL_KEY_TYPE);
hdr.pkt_len = htonl(len);
result = ipc_socket_write_pkt(fd, aligned_pkt.pkt, len, &hdr);
if (result < 0)
return result;
result = process_ack(fd, PKT_CTRL_KEY_TYPE_OK);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: key_type %d not accepted by switch. \n", key_type);
return -1;
}
return 0;
}
static int
process_init_end(int fd)
{
swtchdr hdr;
int result = 0;
hdr.pkt_type = htonl(PKT_CTRL_INIT_END);
hdr.pkt_len = 0;
result = ipc_socket_write_pkt(fd, NULL, 0, &hdr);
if (result < 0) {
return -1;
}
return 0;
}
static int
switch_negotiate(int fd, int ipc_type, int key_type, char *mmap_idstr, int mmap_file_size)
{
int result;
result = process_version(fd);
if (result < 0) {
return -1;
}
result = process_ipc_type(fd, ipc_type, mmap_idstr, mmap_file_size);
if (result < 0) {
return -1;
}
result = process_key_type(fd, key_type);
if (result < 0) {
return -1;
}
result = process_init_end(fd);
if (result < 0) {
return -1;
}
return 0;
}
/*
* some more forward decls --------------------------------------------------
*/
static void mmap_data_table_init(void);
static int ipc_create_mmap_files(const char *mmap_dir, char *mmap_idstr, int mmap_file_size);
int mmap_data_table_initialized = 0;
/*
* the old "make a connection" (either socket or mmap) function
*
* returns the socket fd on success, -1 otherwise.
*/
int
ipc_init(const char *switch_server, const char *ipc_type_string, int key_type, const char *mmap_dir, int mmap_file_size)
{
int mmap_fd, ipc_type, result;
int sockfd, server_port;
struct hostent *server;
struct sockaddr_in serv_addr;
struct utsname host_utsname;
const char *server_name;
char mmap_idstr[MAXPATHLEN];
if (strcmp(ipc_type_string, "socket") == 0) {
ipc_type = PKT_CTRL_IPC_SOCKET;
} else if (strcmp(ipc_type_string, "mmap") == 0) {
ipc_type = PKT_CTRL_IPC_MMAP;
} else if (strcmp(ipc_type_string, "") == 0) {
ipc_type = PKT_CTRL_IPC_SOCKET;
} else {
fprintf(stderr, "ERROR: libswitch: unknown switch ipc type specified: %s \n", ipc_type_string);
return -1;
}
if (uname(&host_utsname) == -1) {
fprintf(stderr, "ERROR: libswitch: uname: %s \n", strerror(errno));
return -1;
}
server_name = host_utsname.nodename;
/* TBD: System with multiple interfaces with different hostnames */
if (switch_server != NULL) {
if (strcmp(switch_server, server_name) != 0) {
if (ipc_type == PKT_CTRL_IPC_MMAP) {
fprintf(stderr, "ERROR:libswitch: mmap can't be used for ipc "
"between the machines %s and %s \n",
switch_server, server_name);
return -1;
}
server_name = switch_server;
}
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
fprintf(stderr, "ERROR: libswitch: socket: %s \n", strerror(errno));
return -1;
}
server = gethostbyname(server_name);
if (server == NULL) {
fprintf(stderr, "ERROR: libswitch: gethostbyname: %s \n", strerror(h_errno));
close(sockfd);
return -1;
}
server_port = SWITCH_DEFAULT_PORT;
bzero((char *) &serv_addr, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(server_port);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
fprintf(stderr, "ERROR: libswitch: connect: %s \n", strerror(errno));
close(sockfd);
return -1;
}
if (ipc_type == PKT_CTRL_IPC_MMAP) {
if (mmap_data_table_initialized == 0)
mmap_data_table_init();
mmap_fd = ipc_create_mmap_files(mmap_dir, mmap_idstr, mmap_file_size);
if (mmap_fd < 0) {
fprintf(stderr, "ERROR: libswitch: failed to create mmap files for ipc \n");
close(sockfd);
return -1;
}
}
result = switch_negotiate(sockfd, ipc_type, key_type, mmap_idstr, mmap_file_size);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: negotiation with switch server %s failed. \n", server_name);
close(sockfd);
return result;
}
if (ipc_type == PKT_CTRL_IPC_MMAP) {
close(sockfd);
info[ mmap_fd ].implt = _MMAP;
result = mmap_fd;
} else {
info[ sockfd ].implt = _SOCKET;
result = sockfd;
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
// //
// The following is for Mmap-based blaze networking... //
// ^^^^ //
////////////////////////////////////////////////////////////////////////////////
#define FIRST_MMAP_FD 1000
#define MMAP_DATA_TABLE_SIZE 32
typedef struct mmap_data {
int fd;
char *snd_mmap_addr;
int snd_mmap_index;
int snd_mmap_size;
char *rcv_mmap_addr;
int rcv_mmap_index;
int rcv_mmap_size;
} mmap_data_t;
mmap_data_t mmap_data_table[MMAP_DATA_TABLE_SIZE];
int mmap_data_table_index;
/*int mmap_data_table_initialized; is moved up above */
mutex_t mmap_data_table_lock;
static void
mmap_data_table_init(void)
{
int index;
mutex_init(&mmap_data_table_lock, USYNC_THREAD, NULL);
for (index=0; index<MMAP_DATA_TABLE_SIZE; index++) {
mmap_data_table[index].fd = -1;
}
mmap_data_table_initialized = 1;
return;
}
static mmap_data_t *
mmap_data_alloc(void)
{
int index;
int found = 0;
mutex_lock(&mmap_data_table_lock);
for (index=0; index<MMAP_DATA_TABLE_SIZE; index++) {
if (mmap_data_table[index].fd == -1) {
mmap_data_table[index].fd = index + FIRST_MMAP_FD;
found = 1;
break;
}
}
mutex_unlock(&mmap_data_table_lock);
if (found == 0)
return NULL;
return &mmap_data_table[index];
}
#define SWITCH_LIB_PAGE_SIZE 8192
static int
ipc_mmap_init_file(int fd, int mmap_file_size)
{
char *buf;
int index, page_count, count;
int result = 0;
page_count = mmap_file_size / SWITCH_LIB_PAGE_SIZE;
buf = (char *) calloc(1, SWITCH_LIB_PAGE_SIZE);
if (buf == NULL) {
fprintf(stderr, "ERROR: libswitch: calloc SWITCH_LIB_PAGE_SIZE returned NULL \n");
return -1;
}
for (index=0; index<page_count; index++) {
result = write(fd, buf, SWITCH_LIB_PAGE_SIZE);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: failed to initialize mmap file : %s \n", strerror(errno));
return -1;
}
}
count = mmap_file_size % SWITCH_LIB_PAGE_SIZE;
for (index=0; index<count; index++) {
result = write(fd, buf, count);
if (result < 0) {
fprintf(stderr, "ERROR: libswitch: failed to initialize mmap file : %s \n", strerror(errno));
return -1;
}
}
free(buf);
return 0;
}
static int
ipc_create_mmap_files(const char *mmap_dir, char *mmap_idstr, int mmap_file_size)
{
int fd, result;
pid_t pid;
caddr_t addr;
char send_filename[MAXPATHLEN];
char recv_filename[MAXPATHLEN];
mmap_data_t *mdp;
pid = getpid();
mdp = mmap_data_alloc();
if (mdp == NULL) {
fprintf(stderr, "ERROR: libswitch: ran out of mmap ipc file descriptors. \n");
return -1;
}
if (mmap_dir == NULL) {
strcpy(send_filename, "/tmp/SAM/send_to_switch.");
} else {
strcpy(send_filename, mmap_dir);
strcat(send_filename, "/send_to_switch.");
}
sprintf(mmap_idstr, "%d_%d", getpid(), mdp->fd);
strcat(send_filename, mmap_idstr);
fd = open(send_filename, O_RDWR|O_CREAT|O_TRUNC, 0777);
if (fd == -1) {
fprintf(stderr, "ERROR: libswitch: open: ( filename %s ) %s \n", send_filename, strerror(errno));
return -1;
}
fchmod(fd, 0777);
result = ipc_mmap_init_file(fd, mmap_file_size);
if (result < 0) {
close(fd);
unlink(send_filename);
return -1;
}
addr = NULL;
addr = mmap(addr, mmap_file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == (caddr_t ) -1) {
close(fd);
unlink(send_filename);
fprintf(stderr, "ERROR: libswitch: mmap(%s): %s \n", send_filename, strerror(errno));
return -1;
}
mdp->snd_mmap_addr = (char *) addr;
mdp->snd_mmap_size = mmap_file_size;
mdp->snd_mmap_index = 0;
close(fd);
if (mmap_dir == NULL) {
strcpy(recv_filename, "/tmp/SAM/recv_from_switch.");
} else {
strcpy(recv_filename, mmap_dir);
strcat(recv_filename, "/recv_from_switch.");
}
strcat(recv_filename, mmap_idstr);
fd = open(recv_filename, O_RDWR|O_CREAT|O_TRUNC, 0777);
if (fd == -1) {
munmap(mdp->snd_mmap_addr, mmap_file_size);
unlink(send_filename);
fprintf(stderr, "ERROR: libswitch: open: ( filename %s ) %s \n", recv_filename, strerror(errno));
return -1;
}
fchmod(fd, 0777);
result = ipc_mmap_init_file(fd, mmap_file_size);
if (result < 0) {
close(fd);
unlink(recv_filename);
munmap(mdp->snd_mmap_addr, mmap_file_size);
unlink(send_filename);
return -1;
}
addr = NULL;
addr = mmap(addr, mmap_file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == (caddr_t ) -1) {
close(fd);
unlink(recv_filename);
unlink(send_filename);
munmap(mdp->snd_mmap_addr, mmap_file_size);
fprintf(stderr, "ERROR: libswitch: mmap(%s): %s \n", recv_filename, strerror(errno));
return -1;
}
mdp->rcv_mmap_addr = (char *) addr;
mdp->rcv_mmap_size = mmap_file_size;
mdp->rcv_mmap_index = 0;
close(fd);
return mdp->fd;
}
#define MMAP_PKT_OFFSET SWITCH_PKT_HDR_SIZE
#define MMAP_CLOSE -1
static int
ipc_mmap_read_pkt(int fd, char *pkt, int maxlen, swtchdr *hdr, int timout)
{
int pktlen;
swtchdr *hptr;
char *src;
mmap_data_t *mdp;
if (0) {
fprintf(stderr, "INFO: ipc_mmap_read_pkt called with timout %d \n", timout);
}
if ((fd - FIRST_MMAP_FD) >= MMAP_DATA_TABLE_SIZE)
return -1;
mdp = &mmap_data_table[fd - FIRST_MMAP_FD];
hptr = (swtchdr *) (mdp->rcv_mmap_addr + mdp->rcv_mmap_index);
for (;;) {
pktlen = hptr->pkt_len;
if (pktlen != 0) {
pktlen = ntohl(pktlen);
if (pktlen == MMAP_CLOSE) {
return -1;
} else {
if (hdr != NULL) {
hdr->pkt_type = ntohl(hptr->pkt_type);
hdr->pkt_len = pktlen;
}
if (pktlen > maxlen) {
fprintf(stderr, "ERROR: libswitch: ipc_mmap_read_pkt: pktlen %d > maxlen %d \n", pktlen, maxlen);
return -1;
}
src = (mdp->rcv_mmap_addr + mdp->rcv_mmap_index) + MMAP_PKT_OFFSET;
bcopy(src, pkt, pktlen);
mdp->rcv_mmap_index += MAX_SWITCH_PKT_SIZE;
if ((mdp->rcv_mmap_index + MAX_SWITCH_PKT_SIZE) > mdp->rcv_mmap_size) {
mdp->rcv_mmap_index = 0;
}
hptr->pkt_len = 0;
return pktlen;
}
}
}
/* NOT REACHED */
}
static int
ipc_mmap_write_pkt(int fd, char *pkt, int pktlen, swtchdr *hdr)
{
char *dst;
mmap_data_t *mdp;
swtchdr *hptr;
if (pktlen > SWITCH_ETHERMTU) {
fprintf(stderr, "ERROR: libswitch: ipc_mmap_write_pkt: pktlen %d > maxlen %d \n", pktlen, SWITCH_ETHERMTU);
return -1;
}
if ((fd - FIRST_MMAP_FD) >= MMAP_DATA_TABLE_SIZE)
return -1;
mdp = &mmap_data_table[fd - FIRST_MMAP_FD];
hptr = (swtchdr *) (mdp->snd_mmap_addr + mdp->snd_mmap_index);
if (hdr == NULL) {
hptr->pkt_type = htonl(PKT_DATA);
} else {
hptr->pkt_type = htonl(hdr->pkt_type);
}
dst = (mdp->snd_mmap_addr + mdp->snd_mmap_index) + MMAP_PKT_OFFSET;
bcopy(pkt, dst, pktlen);
mdp->snd_mmap_index += MAX_SWITCH_PKT_SIZE;
if ((mdp->snd_mmap_index + MAX_SWITCH_PKT_SIZE) > mdp->snd_mmap_size) {
mdp->snd_mmap_index = 0;
}
hptr->pkt_len = htonl(pktlen);
return pktlen;
}
////////////////////////////////////////////////////////////////////////////////
// //
// The following section is for Snoop-based blaze networking... //
// ^^^^^ //
////////////////////////////////////////////////////////////////////////////////
/*#include <dlpi.h> is up above */
/* usage:
* fd = init_snoop_device ("ge", 1); <--- implemented here, this is
* the only non-static ftn.
* getmsg (fd,... <--- std Solaris syscall
* putmsg (fd,... <--- std Solaris syscall
* close (fd)
*/
/* forward decls for this section ------------------------------------
*/
static int snoop_dlattachreq(int fd, ulong_t ppa);
static int snoop_dlokack(int fd, char *bufp);
static int snoop_dlpromisconreq(int fd, ulong_t level);
static int snoop_dlbindreq(int fd, ulong_t sap, ulong_t max_conind,
ulong_t service_mode, ulong_t conn_mgmt, ulong_t xidtest);
static int snoop_dlbindack(int fd, char *bufp);
static int snoop_strioctl(int fd, int cmd, int timout, int len, char *dp);
static int snoop_strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap,
int *flagsp, const char *caller);
static int snoop_expecting (int prim, union DL_primitives *dlp);
static const char * snoop_dlprim(ulong_t prim);
#if 0 /*
* Someday we may have to resurrect this, or maybe we'll get lucky...
* Here is the code that used to be in "main.cc" for parsing the
* "-N 0=ge0,1=ge0" host device snooping specifications. That
* string, without the -N, is now accessable through the function
* call SYSTEM_get_hostconfig ("snoop.args");
*/
int num_matches;
int nic0, nic1, minor0, minor1;
char devname0[32+4];
char devname1[32+4];
char devbuf[64];
char *arg, comma;
if (argndx == argc) {
fprintf(stderr, "ERROR: No argument specified to -N \n");
exit(4);
}
arg = argv[argndx++];
nic0 = minor0 = nic1 = minor1 = 0;
num_matches = sscanf(arg, "%d=%32[a-zA-Z]%d%c%d=%32[a-zA-Z]%d",
&nic0, devname0, &minor0, &comma, &nic1, devname1, &minor1);
if ( (num_matches != 3) && (num_matches != 7) ) {
switch (num_matches) {
case 0:
case 4:
fprintf(stderr, "ERROR: Bad argument (%s) to -N \n", arg);
break;
case 1:
case 5:
fprintf(stderr, "ERROR: Bad argument (%s) to -N. <digit>=<string><digit> expected. \n", arg);
break;
case 2:
case 6:
fprintf(stderr, "ERROR: Bad argument (%s) to -N. device minor number incorrectly specified.\n", arg);
break;
default:
fprintf(stderr, "ERROR: Bad argument (%s) to -N \n", arg);
break;
}
exit(4);
}
if (num_matches > 3) {
if (comma != ',') {
fprintf(stdout, "WARNING: Processing -N argument: comma expected between multiple snoop device specification.\n");
}
}
if (nic0 >= NUM_SIMGE) {
fprintf(stderr, "ERROR: Bad GE NIC number ( %d ) specified. allowed range 0 to %d \n", nic0, (NUM_SIMGE-1));
exit(4);
}
if (num_matches > 4) {
if (nic1 >= NUM_SIMGE) {
fprintf(stderr, "ERROR: Bad GE NIC number ( %d ) specified. allowed range 0 to %d \n", nic1, (NUM_SIMGE-1));
exit(4);
}
}
devbuf[0] = 0;
strcat(devbuf, "/dev/");
strcat(devbuf, devname0);
network_device_to_snoop[nic0] = strdup(devbuf);
if (network_device_to_snoop[nic0] == NULL) {
fprintf(stderr, "ERROR: Ran out of memory while duplicating string %s. \n", devbuf);
exit(4);
}
network_device_minor_num[nic0] = minor0;
fprintf(stdout, "Will snoop device %s%d for NIC ge%d \n", network_device_to_snoop[nic0], minor0, nic0);
if (num_matches == 7) {
devbuf[0] = 0;
strcat(devbuf, "/dev/");
strcat(devbuf, devname1);
network_device_to_snoop[nic1] = strdup(devbuf);
if (network_device_to_snoop[nic1] == NULL) {
fprintf(stderr, "ERROR: Ran out of memory while duplicating string %s. \n", devbuf);
exit(4);
}
network_device_minor_num[nic1] = minor1;
fprintf(stdout, "Will snoop device %s%d for NIC ge%d \n", network_device_to_snoop[nic1], minor1, nic1);
}
#endif
/*
* inputs: name is eg "ce", minor is eg 1, to snoop physical device "ce1".
* returns filedescr, or -1 on error
*/
int init_snoop_device(const char * name, int minor)
{
int fd;
char buf[MAXDLBUF];
/* Open the device */
if ((fd = open(name, O_RDWR)) < 0) {
printf("FYI: Unable to snoop \"%s\" (try runing as root?)\n", name);
return -1;
}
/* Attach */
if (snoop_dlattachreq(fd, minor) < 0) goto nogood;
if (snoop_dlokack(fd, buf) < 0) goto nogood;
/* pick up everything on the wire */
#if 0
if (snoop_dlpromisconreq(fd, DL_PROMISC_PHYS) < 0) goto nogood;
#endif
if (snoop_dlpromisconreq(fd, DL_PROMISC_SAP) < 0) goto nogood;
if (snoop_dlokack(fd, buf) < 0) goto nogood;
/* Bind: use 2 as sap for token ring and 0 for else */
if (snoop_dlbindreq(fd, 0, 0, DL_CLDLS, 0, 0) < 0) goto nogood;
if (snoop_dlbindack(fd, buf) < 0) goto nogood;
if (snoop_strioctl(fd, DLIOCRAW, -1, 0, (char *) NULL) < 0) {
perror("init_snoop_device: DLIOCRAW");
goto nogood;
}
/* Flush the read side of the Stream */
if (ioctl(fd, I_FLUSH, FLUSHR) < 0) {
perror("init_snoop_device: I_FLUSH");
goto nogood;
}
good:
#if 0
{
char hostname[256];
struct hostent *hp;
uint_t host_ipaddr;
/* get host ipaddr for filtering */
/* this looks bogus, what if the host machine has multiple NICs... */
gethostname(hostname, sizeof (hostname));
if ((hp = gethostbyname(hostname)) == NULL) {
perror("gethostbyname error\n");
return;
}
*** this needs to be saved (for use by getmsg) in the info[fd] array ***
host_ipaddr = *(uint_t *) hp->h_addr;
}
#endif
return fd;
nogood:
close (fd);
return -1;
}
int snoop_getmsg (int fd, char * buf, int maxlen)
{
return -1;
}
int snoop_putmsg (int fd, char * buf, int len)
{
return -1;
}
/* dlpi routines */
static int
snoop_dlattachreq(int fd, ulong_t ppa)
{
dl_attach_req_t attach_req;
int flags;
struct strbuf ctl;
attach_req.dl_primitive = DL_ATTACH_REQ;
attach_req.dl_ppa = ppa;
ctl.maxlen = 0;
ctl.len = sizeof(attach_req);
ctl.buf = (char *) &attach_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) {
perror("dlattachreq: putmsg");
return -1;
}
return 0;
}
static int
snoop_dlokack(int fd, char *bufp)
{
int flags;
struct strbuf ctl;
union DL_primitives *dlp;
ctl.maxlen = MAXDLBUF;
ctl.len = 0;
ctl.buf = bufp;
if (snoop_strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack")
< 0)
return -1;
dlp = (union DL_primitives *) ctl.buf;
if (snoop_expecting(DL_OK_ACK, dlp) < 0)
return -1;
if (ctl.len < sizeof(dl_ok_ack_t)) {
vargs_error(0, "snoop_dlokack: response len too short: %d", ctl.len);
return -1;
}
if (flags != RS_HIPRI) {
vargs_error(0, "snoop_dlokack: DL_OK_ACK was not M_PCPROTO");
return -1;
}
return 0;
}
static int
snoop_dlpromisconreq(int fd, ulong_t level)
{
dl_promiscon_req_t promiscon_req;
int flags;
struct strbuf ctl;
promiscon_req.dl_primitive = DL_PROMISCON_REQ;
promiscon_req.dl_level = level;
ctl.maxlen = 0;
ctl.len = sizeof(promiscon_req);
ctl.buf = (char *) &promiscon_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) {
vargs_error(0, "snoop_dlpromisconreq: putmsg");
return -1;
}
return 0;
}
void
snoop_dlpromiscoff(int fd, ulong_t level)
{
dl_promiscoff_req_t promiscoff_req;
struct strbuf ctl;
int flags;
promiscoff_req.dl_primitive = DL_PROMISCOFF_REQ;
promiscoff_req.dl_level = level;
ctl.maxlen = 0;
ctl.len = sizeof (promiscoff_req);
ctl.buf = (char *) &promiscoff_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0)
vargs_error(0, "dlpromiscoff: putmsg");
return;
}
static int
snoop_dlbindreq(int fd,
ulong_t sap,
ulong_t max_conind,
ulong_t service_mode,
ulong_t conn_mgmt,
ulong_t xidtest)
{
dl_bind_req_t bind_req;
int flags;
struct strbuf ctl;
bind_req.dl_primitive = DL_BIND_REQ;
bind_req.dl_sap = sap;
bind_req.dl_max_conind = max_conind;
bind_req.dl_service_mode = service_mode;
bind_req.dl_conn_mgmt = conn_mgmt;
bind_req.dl_xidtest_flg = xidtest;
ctl.maxlen = 0;
ctl.len = sizeof (bind_req);
ctl.buf = (char *) &bind_req;
flags = 0;
if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) {
perror("snoop_dlbindreq: putmsg");
return -1;
}
return 0;
}
static int
snoop_dlbindack(int fd, char *bufp)
{
int flags;
struct strbuf ctl;
union DL_primitives *dlp;
ctl.maxlen = MAXDLBUF;
ctl.len = 0;
ctl.buf = bufp;
if (snoop_strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack")
< 0)
return -1;
dlp = (union DL_primitives *) ctl.buf;
if (snoop_expecting(DL_BIND_ACK, dlp) < 0)
return -1;
if (flags != RS_HIPRI) {
vargs_error(0, "dlbindack: DL_OK_ACK was not M_PCPROTO");
return -1;
}
if (ctl.len < sizeof (dl_bind_ack_t)) {
vargs_error(0, "dlbindack: short response ctl.len: %d", ctl.len);
return -1;
}
return 0;
}
static int
snoop_strioctl(int fd, int cmd, int timout, int len, char *dp)
{
int rc;
struct strioctl sioc;
sioc.ic_cmd = cmd;
sioc.ic_timout = timout;
sioc.ic_len = len;
sioc.ic_dp = dp;
rc = ioctl(fd, I_STR, &sioc);
if (rc < 0)
return (rc);
else
return (sioc.ic_len);
}
#define MAXWAIT 15
#ifdef __cplusplus
extern "C" {
#endif
static void snoop_sigalrm (int);
#ifdef __cplusplus
}
#endif
static void
snoop_sigalrm(int i)
{
(void) printf("snoop_sigalrm (%d) : TIMEOUT", i);
return;
}
static int
snoop_strgetmsg(int fd,
struct strbuf *ctlp,
struct strbuf *datap,
int *flagsp,
const char *caller)
{
int rc;
char errmsg[80];
/*
* Start timer.
*/
(void) signal(SIGALRM, snoop_sigalrm);
if (alarm(MAXWAIT) < 0) {
(void) snprintf(errmsg, 80, "%s: alarm", caller);
perror(errmsg);
return -1;
}
/*
* Set flags argument and issue getmsg().
*/
*flagsp = 0;
if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
(void) snprintf(errmsg, 80, "%s: getmsg", caller);
perror(errmsg);
return -1;
}
/*
* Stop timer.
*/
if (alarm(0) < 0) {
(void) snprintf(errmsg, 80, "%s: alarm", caller);
perror(errmsg);
return -1;
}
/*
* Check for MOREDATA and/or MORECTL.
*/
if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) {
vargs_error(0, "%s: MORECTL|MOREDATA", caller);
return -1;
}
if (rc & MORECTL) {
vargs_error(0, "%s: MORECTL", caller);
return -1;
}
if (rc & MOREDATA) {
vargs_error(0, "%s: MOREDATA", caller);
return -1;
}
/*
* Check for at least sizeof(long) control data portion.
*/
if (ctlp->len < sizeof(long)) {
vargs_error(0, "getmsg: control portion length < sizeof(long): %d", ctlp->len);
return -1;
}
return 0;
}
static const char *
snoop_dlprim(ulong_t prim)
{
#define CASERET(s) case s: return (#s)
switch ((int)prim) {
CASERET(DL_INFO_REQ);
CASERET(DL_INFO_ACK);
CASERET(DL_ATTACH_REQ);
CASERET(DL_DETACH_REQ);
CASERET(DL_BIND_REQ);
CASERET(DL_BIND_ACK);
CASERET(DL_UNBIND_REQ);
CASERET(DL_OK_ACK);
CASERET(DL_ERROR_ACK);
CASERET(DL_SUBS_BIND_REQ);
CASERET(DL_SUBS_BIND_ACK);
CASERET(DL_UNITDATA_REQ);
CASERET(DL_UNITDATA_IND);
CASERET(DL_UDERROR_IND);
CASERET(DL_UDQOS_REQ);
CASERET(DL_CONNECT_REQ);
CASERET(DL_CONNECT_IND);
CASERET(DL_CONNECT_RES);
CASERET(DL_CONNECT_CON);
CASERET(DL_TOKEN_REQ);
CASERET(DL_TOKEN_ACK);
CASERET(DL_DISCONNECT_REQ);
CASERET(DL_DISCONNECT_IND);
CASERET(DL_RESET_REQ);
CASERET(DL_RESET_IND);
CASERET(DL_RESET_RES);
CASERET(DL_RESET_CON);
default:
fprintf(stderr, "snoop_dlprim: unknown primitive 0x%x", prim);
return "unknown primitive";
}
}
static int
snoop_expecting (int prim, union DL_primitives *dlp)
{
if (dlp->dl_primitive != (ulong_t) prim) {
fprintf(stderr, "Failed to initialize device for snooping \n");
vargs_error(0, "expected %s got %s", snoop_dlprim(prim),
snoop_dlprim(dlp->dl_primitive));
return -1;
}
return 0;
}
void
vargs_error(int thresh, const char *fmt, ...)
{
if (netsim_debug >= thresh) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
(void) fprintf(stderr, "\n");
va_end(ap);
/* exit(1); NOT ACCEPTABLE !!! */
}
}
////////////////////////////////////////////////////////////////////////////////
// //
// The following section is for Common utility functions //
// //
////////////////////////////////////////////////////////////////////////////////
/*
* this ether_cksum function was copied from Gigabit Ethernet driver,
* which does not inspire much confidence, look at the mish-mash of
* indexed and pointed memory references...
*/
#if 1
uint16_t
ether_chksum(char *buf, int offset, int len)
{
int index, count;
uint_t csum;
ushort_t *shortP, value;
value = 0;
csum = 0;
count = len & (~0x1);
shortP = (ushort_t *) (buf + offset);
if (((unsigned long ) shortP) & 0x1) {
fprintf(stderr, "ERROR: ether_cksum odd address. src %p \n", shortP);
return -1; exit(1);
}
for (index=offset; index<(count+offset); index += sizeof(ushort_t))
csum += *shortP++;
if (len & 0x1) {
value = buf[index] << 8;
csum += value;
}
while (csum>>16)
csum = (csum & 0xffff) + (csum >> 16);
csum = ~csum;
csum = csum & 0x0000FFFF;
return csum;
}
#else
//
// a little bit simpler version!
//
uint16_t
ether_chksum(char *buf, int offset, int len)
{
ushort_t *p = (ushort_t*) (buf + offset);
int csum = 0;
// add up all the whole double-bytes...
for (; len>1; len-=2)
csum += *p++;
// and if there is an odd byte at the end...
if (len)
csum += (*((unsigned char *)p)) & 0xff00; // big-endian !!!
// now wrap around the checksum overflow
while (csum>>16)
csum = (csum & 0xffff) + (csum >> 16);
// finish by taking the 1's complement
csum = (~csum) & 0x0000ffff;
return csum;
}
#endif
//
// TCP checksum including the "pseudo header" of fields from ip header
//
int tcp_chksum (ethpacket_t * pkt)
{
ushort_t *p = (ushort_t*) pkt;
int hdrcsum = 0;
int csum = 0;
int bytes = ETHHDRSZ + pkt->u.ip.iplen; // Total pkt-len Incl ether hdr.
int i;
// IP protocol
csum = (p[11] & 0x00ff);
// IP data length
csum += (pkt->u.ip.iplen - 4*(pkt->u.ip.verlen & 0x0f));
// IP src and dest addrs
csum += p[13] + p[14] + p[15] + p[16];
// now the TCP header and data
for (i=17; i<(bytes/2); i++)
csum += p[i];
// and if there is an odd byte at the end...
if (bytes & 0x1)
csum += p[i] & 0xff00;
// now wrap the checksum overflow back around
while (csum>>16)
csum = (csum & 0xffff) + (csum >> 16);
// finish by taking the 1's complement
csum = (~csum) & 0x0000ffff;
return csum;
}
// compute the raw checksum of just the tcp pseudo header
//
int tcp_pseudocs (ethpacket_t * pkt)
{
ushort_t *p = (ushort_t*) pkt;
int csum = 0;
int bytes = ETHHDRSZ + pkt->u.ip.iplen; // Total pkt-len Incl ether hdr.
int i;
// IP protocol
csum = (p[11] & 0x00ff);
// IP data length
csum += (pkt->u.ip.iplen - 4*(pkt->u.ip.verlen & 0x0f));
// IP src and dest addrs
csum += p[13] + p[14] + p[15] + p[16];
// now wrap the checksum overflow back around
while (csum>>16)
csum = (csum & 0xffff) + (csum >> 16);
// raw checksum, no 1's complement of the result here...
return csum;
}
uint16_t
ether_reverse_hword(uint16_t value)
{
char reverse_buf[2];
reverse_buf[0] = value & 0x00FF;
reverse_buf[1] = (value >> 8) & 0x00FF;
return *(uint16_t *) reverse_buf;
}
uint32_t
ether_reverse_word(uint32_t value)
{
char reverse_buf[4];
reverse_buf[0] = value & 0x00FF;
reverse_buf[1] = (value >> 8) & 0x00FF;
reverse_buf[2] = (value >> 16) & 0x00FF;
reverse_buf[3] = (value >> 24) & 0x00FF;
return *(uint32_t *) reverse_buf;
}
uint64_t
ether_reverse_lword(uint64_t value)
{
char reverse_buf[8];
reverse_buf[0] = value & 0x00FF;
reverse_buf[1] = (value >> 8) & 0x00FF;
reverse_buf[2] = (value >> 16) & 0x00FF;
reverse_buf[3] = (value >> 24) & 0x00FF;
reverse_buf[4] = (value >> 32) & 0x00FF;
reverse_buf[5] = (value >> 40) & 0x00FF;
reverse_buf[6] = (value >> 48) & 0x00FF;
reverse_buf[7] = (value >> 56) & 0x00FF;
return *(uint64_t *) reverse_buf;
}
// ---------------------------------------------------------------------------
// SNOOP
// debug levels: 0= nada; 1= decoded; 2= raw hex
//
int
netsim_snoop (char * buf, int buflen, int dbglevel)
{
int cs, pktlen;
if (dbglevel >= 1) { // -------------------------------
ethpacket_t * packet = (ethpacket_t*) buf;
int et,ipp;
et = eth_type(packet);
fprintf (stderr, "eth/%d{mac:%02x->%02x, ", buflen,
eth_srcMAC(packet) & 0xff,
eth_dstMAC(packet) & 0xff);
switch (et) {
case ET_IPv4: {
pktlen = ETHHDRSZ + packet->u.ip.iplen;
fprintf(stderr,"ip/%d", packet->u.ip.iplen);
ipp = eth_ipproto(packet);
fprintf (stderr, "{ip:%d->%d, ",
eth_srcIP(packet) & 0xff,
eth_dstIP(packet) & 0xff);
switch (ipp) {
case IP_TCP: {
fprintf(stderr,"tcp");
if (eth_tcpzlenack(packet)) { /* happens a lot */
fprintf(stderr, "-ack(%s)\n",
eth_tcp_string(eth_srcPORT(packet)));
break;
}
if (packet->u.ip.u.tcp.flags & 0x0002/*TCP_SYN_FLAG*/)
fprintf(stderr, "connect(");
if (packet->u.ip.u.tcp.flags & 0x0001/*TCP_FIN_FLAG*/)
fprintf(stderr, "disconn(");
fprintf(stderr," %s->%d",
eth_tcp_string(eth_srcPORT(packet)),
eth_tcp_string(eth_dstPORT(packet)));
if (packet->u.ip.u.tcp.flags & 0x0002/*TCP_SYN_FLAG*/)
fprintf(stderr, ")");
if (packet->u.ip.u.tcp.flags & 0x0001/*TCP_FIN_FLAG*/)
fprintf(stderr, ")");
if ((cs = tcp_chksum (packet)) != 0)
fprintf(stderr," ***chksum error 0x%04x*** ", cs);
} break;
case IP_UDP: {
fprintf(stderr,"udp/%d", packet->u.ip.u.udp.udplen);
fprintf(stderr," %d->%d", packet->u.ip.u.udp.srcPORT,
packet->u.ip.u.udp.dstPORT);
if ((cs = tcp_chksum (packet)) != 0)
fprintf(stderr," ***chksum error 0x%04x*** ", cs);
} break;
case IP_IP: {
fprintf(stderr," %s", "ip-encaps");/*ip-encapsulation*/
} break;
case IP_ICMP: {
fprintf(stderr," %s", eth_icmp_string (
eth_icmptype (packet), eth_icmpcode (packet)));
} break;
case IP_IGMP: {
fprintf(stderr," %s", "igmp");/*gateway-management-prot*/
} break;
case IP_GGP: {
fprintf(stderr," %s", "ggp");/*gateway-to-gateway-prot*/
} break;
case IP_EGP: {
fprintf(stderr," %s", "ext-gateway");/*exterior-gw-prot*/
} break;
case IP_IGP: {
fprintf(stderr," %s", "int-gateway");/*interior-gw-prot*/
} break;
default: { // unknown ip-header-protocol field
fprintf (stderr, " %d", ipp);
} break;
}
} break;
case ET_ARP: {
pktlen = ETHHDRSZ + sizeof(packet->u.arp);
if (packet->u.arp.op == 1)
fprintf (stderr, "arp rqst %d", packet->u.arp.dstIP[3]);
else if (packet->u.arp.op == 2)
fprintf (stderr, "arp reply %d", packet->u.arp.dstIP[3]);
else
fprintf (stderr, "arp ???");
} break;
case ET_RARP: {
pktlen = ETHHDRSZ + sizeof(packet->u.arp);
if (packet->u.arp.op == 3)
fprintf (stderr, "rarp rqst %d", packet->u.arp.dstIP[3]);
else if (packet->u.arp.op == 4)
fprintf (stderr, "rarp reply %d",packet->u.arp.dstIP[3]);
else
fprintf (stderr, "rarp ???");
} break;
default: { // unknown ether-header-type field
pktlen = buflen;
fprintf (stderr, " %d", et);
} break;
}
fprintf (stderr, "}\n");
}
if (dbglevel >= 2) {
if (pktlen != buflen) {
fprintf(stderr,"------ length of buf/%d != pkt/%d \n", buflen, pktlen);
}
}
if (dbglevel >= 3) { // --------------------------------------
int i;
fprintf(stderr, "\n");
for (i=0; i<pktlen; i+=16) {
int j,lim;
char out[17];
lim = pktlen-i;
if (lim>16) lim = 16;
fprintf(stderr, "%04x :",i);
for (j=0; j<lim; j++) {
int c = *(unsigned char *)&(buf[i+j]);
fprintf(stderr, " %02x",c);
out[j] = c>=32 && c<127 ? c : '.';
}
out[j] = 0;
for (; j<16; j++) fprintf(stderr, " ..");
fprintf(stderr, "\t: %s\n",out);
}
fprintf(stderr, "\n");
}
if (dbglevel >= 4) { // --------------------------------------
}
return 1;
}
// these aren't appropriate for inlining, but they have to go somewhere...
const char * eth_tcp_string (int tcptype)
{
static char buf[10];
switch (tcptype) {
case TCP_ECHO: return "echo";
case TCP_FTPDATA: return "ftpdata";
case TCP_FTP: return "ftp";
case TCP_SSH: return "ssh";
case TCP_TELNET: return "telnet";
case TCP_TIME: return "time";
case TCP_DNS: return "dns";
case TCP_HTTP: return "http";
case TCP_HTTPS: return "https";
case TCP_SUNRPC: return "sunrpc";
case TCP_LDAP: return "ldap";
case TCP_REXEC: return "rexec";
case TCP_RLOGIN: return "rlogin";
case TCP_RSHELL: return "rshell";
default:
sprintf(&buf[0], "%d", tcptype);
return &buf[0];
}
}
const char * eth_icmp_string (int icmptype, int icmpcode)
{
static char buf[10];
switch (icmptype) {
case ICMP_ECHOREPLY: return "echo_reply";
case ICMP_UNREACHABLE:
switch (icmpcode) {
case 0: return "network_unreachable";
case 1: return "host_unreachable";
case 2: return "protocol_unreachable";
case 3: return "port_unreachable";
case 4: return "fragment_unreachable";
case 5: return "route_unreachable";
case 6: return "network_unknown";
case 7: return "host_unknown";
case 8: return "obsolete_unreachable";
case 9: return "network_prohibited";
case 10: return "host_prohibited";
case 11: return "network_tos_unreachable";
case 12: return "host_tos_unreachable";
default: return "unreachable_?";
}
case ICMP_SRCQUENCH: return "source_quench";
case ICMP_REDIRECT:
switch (icmpcode) {
case 0: return "redirect_network";
case 1: return "redirect_host";
case 2: return "redirect_tos_network";
case 3: return "redirect_tos_host";
default: return "redirect_?";
}
case ICMP_ECHO: return "echo_request";
case ICMP_ROUTERREPLY: return "router_reply";
case ICMP_ROUTERREQST: return "router_request";
case ICMP_TIMEOUT: return "time_exceeded";
case ICMP_INVARG: return "invalid_parameter";
case ICMP_TIMESTAMP: return "timestamp_request";
case ICMP_TSTAMPREPLY: return "timestamp_reply";
case ICMP_INFO: return "obsolete_request";
case ICMP_INFOREPLY: return "obsolete_reply";
case ICMP_MASK: return "mask_request";
case ICMP_MASKREPLY: return "mask_reply";
default:
sprintf(&buf[0], "%d", icmptype);
return &buf[0];
}
}