// ========== 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 ============================================
* "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:
* 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:
* currently we only support socket-based, and it is not yet part of the
/* --->> NOTICE THERE ARE NO BLAZE INCLUDES HERE, KEEP IT THAT WAY !!! <<--- */
/* standard C includes */
/* unix/Solaris includes */
#include <netinet/if_ether.h>
#include <stropts.h> /* strbuf, getmsg, putmsg */
#include <sys/dlpi.h> /* dlbindack, dlokack, etc..., DL_ defines... */
/* this file's public include */
#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_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
, ...);
switch_negotiate(int fd
, int ipc_type
, int key_type
, char *mmap_idstr
, int mmap_file_size
);
ipc_socket_read_pkt(int fd
, char *pkt
, int maxlen
, swtchdr
*hdr
, int timout
);
ipc_mmap_read_pkt(int fd
, char *pkt
, int maxlen
, swtchdr
*hdr
, int timout
);
ipc_socket_write_pkt(int fd
, char *pkt
, int len
, swtchdr
*hdr
);
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
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 */
struct sockaddr_in serv_addr
;
if (strncmp (switchandport
, "sync:", 5) == 0) { // "sync:"
} else if (strncmp (switchandport
, "switch:", 7) == 0) { // "switch:"
if ((q
= strrchr (p
, '/')) != NULL
) { // "host/port"
strncpy (host
, p
, MAXPATHLEN
);
portno
= atoi (&host
[ i
+1 ]);
strncpy (host
, p
, MAXPATHLEN
);
portno
= SYNC_DEFAULT_PORT
;
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--*/
fprintf(stderr
, "ERROR: tryconnect: socket: %s \n", strerror(errno
));
server
= gethostbyname(host
); /*--GETHOSTBYNAME--*/
fprintf(stderr
, "ERROR: tryconnect: gethostbyname: %s \n", strerror(h_errno
));
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
));
if (netsim_debug
) fprintf(stderr
,"netsim_connect fd = %d\n", sockfd
);
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",
info
[ sockfd
].service
= _SWITCH
;
info
[ sockfd
].implt
= _SOCKET
;
/* ------------------------------ simplified ----------------------------------
int netsim_close (int fd
)
info
[ fd
].service
= _NONE
;
info
[ fd
].implt
= _CLOSED
;
/* ------------------------------ simplified ----------------------------------
* result = 0 connection closed, or errno = EINTR (from SIGUSR1)
* result > 0 success, --> actual bytes read is returned in hdr->pkt_len <--
netsim_getmsg(int fd
, char * buf
, int maxlen
, swtchdr
* hdr
)
if (info
[ fd
].service
== _SYNC
) {
if (info
[fd
].implt
== _SOCKET
) {
/* ? read, ? fread, ? fgets, ? getmsg, ? ... */
result
= getmsg (fd
, NULL
/*ctrlbuf*/, &databuf
, NULL
/*flagsp*/);
/* man says 0 => full message copied,
+ => partial message copied,
but does not say how to tell if connection closed remotely ??? */
result
= databuf
.len
; /* actual num bytes copied */
result
= 0; /* our convention */
perror ("netsim: get sync msg: ");
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);
/* ------------------------------ simplified ----------------------------------
* result < 0 error, including connection closed, errno = EINTR (SIGUSR1) ???
* result > 0 success, result bytes written
netsim_putmsg(int fd
, char * buf
, int len
, swtchdr
* hdr
)
if (info
[fd
].service
== _SYNC
) {
if (info
[fd
].implt
== _SOCKET
) {
/* ? write, ? fwrite, ? fputs, ? putmsg, ? ... */
result
= putmsg (fd
, NULL
/*ctrlbuf*/, &databuf
, NULL
/*flagsp*/);
/* man says 0 => full message copied,
but does not say how to tell if connection closed remotely ??? */
result
= databuf
.len
; /* actual num bytes copied */
result
= -1; /* our (confused!) convention */
perror ("netsim: put sync msg: ");
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
){
result
= putmsg(fd
, NULL
, &data
, 0);
if (result
== 0) result
= sizeof(hdr
) + len
;
////////////////////////////////////////////////////////////////////////////////
// The following section is for Socket-based //
////////////////////////////////////////////////////////////////////////////////
#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.
read_all(int fd
, char *buf
, int len
, int timout
)
/* 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
.events
= POLLIN
| POLLRDNORM
| POLLRDBAND
| POLLPRI
;
result
= poll(&pfd
, 1, timout
);
* 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 => that many bytes read
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
);
fprintf(stderr
, "ERROR: read_all: poll: %s \n", strerror(errno
));
result
= read(fd
, &buf
[index
], len
);
fprintf(stderr
, "ERROR: read_all: read: %s \n", strerror(errno
));
} else if (result
== 0) {
fprintf(stderr
, "INFO: read_all: read: connection closed on the socket. \n");
* this is the "socket closed from the other end" case
write_all(int fd
, char *buf
, int len
)
result
= write(fd
, &buf
[index
], len
);
fprintf(stderr
, "ERROR: write_all: write: %s \n", strerror(errno
));
} else if (result
== 0) {
fprintf(stderr
, "ERROR: write_all: write: returned 0 instead of writing %d bytes \n", len
);
* 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.
ipc_socket_read_pkt(int fd
, char *pkt
, int maxlen
, swtchdr
*hdr
, int timout
)
swtchdr local_hdr
, *hptr
;
result
= read_all(fd
, (char *) hptr
, sizeof(swtchdr
), timout
);
if (result
<= 0) { /* was < 0), PAL*/
assert(result
== sizeof(swtchdr
));
pktlen
= ntohl(hptr
->pkt_len
); /* real eth-packet len from switchsim hdr */
fprintf(stderr
, "ERROR: ipc_socket_read_pkt: pktlen %d > maxlen %d \n",
hptr
->pkt_len
= pktlen
; /*PAL*/
return pktlen
+ sizeof(swtchdr
); /*was: pktlen; PAL*/
if ((result
= read_all(fd
, pkt
, pktlen
, timout
)) <= 0) { /*was: <0, PAL*/
assert(result
== pktlen
);
hptr
->pkt_len
= pktlen
; /*PAL*/
return pktlen
+ sizeof(swtchdr
); /*was: pktlen; PAL*/
* result = result-from-writev-syscall:
* >0 success, number of bytes written
ipc_socket_write_pkt(int fd
, char *pkt
, int len
, swtchdr
*hdr
)
swtchdr
*hptr
, local_hdr
;
hptr
->pkt_type
= htonl(PKT_DATA
);
hptr
->pkt_len
= htonl(len
);
wiovec
[0].iov_base
= (char *) hptr
;
wiovec
[0].iov_len
= sizeof(swtchdr
);
result
= writev(fd
, wiovec
, 1);
wiovec
[1].iov_base
= pkt
;
result
= writev(fd
, wiovec
, 2);
/* more swtchdr stuff -------------------------------------- */
typedef struct pkt_ctrl_ipc_type
{
typedef struct pkt_ctrl_key_type
{
process_ack(int fd
, int type
)
char pkt
[SWITCH_ETHERMTU
];
result
= ipc_socket_read_pkt(fd
, pkt
, SWITCH_ETHERMTU
, &hdr
, TIMEOUT_FIVE_SECOND
);
if (result
<= 0) { /* was < 0, PAL*/
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
)
char pkt
[SWITCH_ETHERMTU
];
uint64_t force_alignment
;
ptr
= (char *) SIMGE_SWITCH_VERSION
;
xdrmem_create(&xdr
, aligned_pkt
.pkt
, SWITCH_ETHERMTU
, XDR_ENCODE
);
result
= xdr_string(&xdr
, &ptr
, 64);
hdr
.pkt_type
= htonl(PKT_CTRL_VERSION
);
hdr
.pkt_len
= htonl(len
);
result
= ipc_socket_write_pkt(fd
, aligned_pkt
.pkt
, len
, &hdr
);
result
= process_ack(fd
, PKT_CTRL_VERSION_OK
);
fprintf(stderr
, "ERROR: libswitch: version %s not accepted by switch. \n", ptr
);
process_ipc_type(int fd
, int ipc_type
, char *mmap_idstr
, int mmap_file_size
)
char pkt
[SWITCH_ETHERMTU
];
uint64_t force_alignment
;
pkt_ctrl_ipc_type_t data
;
data
.ipc_type
= ipc_type
;
data
.mmap_idstr
= (char *) "";
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
);
result
= xdr_string(&xdr
, &data
.mmap_idstr
, MAXPATHLEN
);
result
= xdr_int(&xdr
, &data
.mmap_file_size
);
hdr
.pkt_type
= htonl(PKT_CTRL_IPC_TYPE
);
hdr
.pkt_len
= htonl(len
);
result
= ipc_socket_write_pkt(fd
, aligned_pkt
.pkt
, len
, &hdr
);
result
= process_ack(fd
, PKT_CTRL_IPC_TYPE_OK
);
fprintf(stderr
, "ERROR: libswitch: ipc_type %d not accepted by switch. \n", ipc_type
);
process_key_type(int fd
, int key_type
)
char pkt
[SWITCH_ETHERMTU
];
uint64_t force_alignment
;
pkt_ctrl_key_type_t data
;
data
.key_type
= key_type
;
xdrmem_create(&xdr
, aligned_pkt
.pkt
, SWITCH_ETHERMTU
, XDR_ENCODE
);
result
= xdr_int(&xdr
, &data
.key_type
);
hdr
.pkt_type
= htonl(PKT_CTRL_KEY_TYPE
);
hdr
.pkt_len
= htonl(len
);
result
= ipc_socket_write_pkt(fd
, aligned_pkt
.pkt
, len
, &hdr
);
result
= process_ack(fd
, PKT_CTRL_KEY_TYPE_OK
);
fprintf(stderr
, "ERROR: libswitch: key_type %d not accepted by switch. \n", key_type
);
hdr
.pkt_type
= htonl(PKT_CTRL_INIT_END
);
result
= ipc_socket_write_pkt(fd
, NULL
, 0, &hdr
);
switch_negotiate(int fd
, int ipc_type
, int key_type
, char *mmap_idstr
, int mmap_file_size
)
result
= process_version(fd
);
result
= process_ipc_type(fd
, ipc_type
, mmap_idstr
, mmap_file_size
);
result
= process_key_type(fd
, key_type
);
result
= process_init_end(fd
);
* 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.
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
;
struct sockaddr_in serv_addr
;
struct utsname host_utsname
;
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
;
fprintf(stderr
, "ERROR: libswitch: unknown switch ipc type specified: %s \n", ipc_type_string
);
if (uname(&host_utsname
) == -1) {
fprintf(stderr
, "ERROR: libswitch: uname: %s \n", strerror(errno
));
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
);
server_name
= switch_server
;
sockfd
= socket(AF_INET
, SOCK_STREAM
, 0);
fprintf(stderr
, "ERROR: libswitch: socket: %s \n", strerror(errno
));
server
= gethostbyname(server_name
);
fprintf(stderr
, "ERROR: libswitch: gethostbyname: %s \n", strerror(h_errno
));
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
));
if (ipc_type
== PKT_CTRL_IPC_MMAP
) {
if (mmap_data_table_initialized
== 0)
mmap_fd
= ipc_create_mmap_files(mmap_dir
, mmap_idstr
, mmap_file_size
);
fprintf(stderr
, "ERROR: libswitch: failed to create mmap files for ipc \n");
result
= switch_negotiate(sockfd
, ipc_type
, key_type
, mmap_idstr
, mmap_file_size
);
fprintf(stderr
, "ERROR: libswitch: negotiation with switch server %s failed. \n", server_name
);
if (ipc_type
== PKT_CTRL_IPC_MMAP
) {
info
[ mmap_fd
].implt
= _MMAP
;
info
[ sockfd
].implt
= _SOCKET
;
////////////////////////////////////////////////////////////////////////////////
// The following is for Mmap-based blaze networking... //
////////////////////////////////////////////////////////////////////////////////
#define FIRST_MMAP_FD 1000
#define MMAP_DATA_TABLE_SIZE 32
typedef struct mmap_data
{
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
;
mmap_data_table_init(void)
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;
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
;
mutex_unlock(&mmap_data_table_lock
);
return &mmap_data_table
[index
];
#define SWITCH_LIB_PAGE_SIZE 8192
ipc_mmap_init_file(int fd
, int mmap_file_size
)
int index
, page_count
, count
;
page_count
= mmap_file_size
/ SWITCH_LIB_PAGE_SIZE
;
buf
= (char *) calloc(1, SWITCH_LIB_PAGE_SIZE
);
fprintf(stderr
, "ERROR: libswitch: calloc SWITCH_LIB_PAGE_SIZE returned NULL \n");
for (index
=0; index
<page_count
; index
++) {
result
= write(fd
, buf
, SWITCH_LIB_PAGE_SIZE
);
fprintf(stderr
, "ERROR: libswitch: failed to initialize mmap file : %s \n", strerror(errno
));
count
= mmap_file_size
% SWITCH_LIB_PAGE_SIZE
;
for (index
=0; index
<count
; index
++) {
result
= write(fd
, buf
, count
);
fprintf(stderr
, "ERROR: libswitch: failed to initialize mmap file : %s \n", strerror(errno
));
ipc_create_mmap_files(const char *mmap_dir
, char *mmap_idstr
, int mmap_file_size
)
char send_filename
[MAXPATHLEN
];
char recv_filename
[MAXPATHLEN
];
fprintf(stderr
, "ERROR: libswitch: ran out of mmap ipc file descriptors. \n");
strcpy(send_filename
, "/tmp/SAM/send_to_switch.");
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);
fprintf(stderr
, "ERROR: libswitch: open: ( filename %s ) %s \n", send_filename
, strerror(errno
));
result
= ipc_mmap_init_file(fd
, mmap_file_size
);
addr
= mmap(addr
, mmap_file_size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
if (addr
== (caddr_t
) -1) {
fprintf(stderr
, "ERROR: libswitch: mmap(%s): %s \n", send_filename
, strerror(errno
));
mdp
->snd_mmap_addr
= (char *) addr
;
mdp
->snd_mmap_size
= mmap_file_size
;
strcpy(recv_filename
, "/tmp/SAM/recv_from_switch.");
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);
munmap(mdp
->snd_mmap_addr
, mmap_file_size
);
fprintf(stderr
, "ERROR: libswitch: open: ( filename %s ) %s \n", recv_filename
, strerror(errno
));
result
= ipc_mmap_init_file(fd
, mmap_file_size
);
munmap(mdp
->snd_mmap_addr
, mmap_file_size
);
addr
= mmap(addr
, mmap_file_size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
if (addr
== (caddr_t
) -1) {
munmap(mdp
->snd_mmap_addr
, mmap_file_size
);
fprintf(stderr
, "ERROR: libswitch: mmap(%s): %s \n", recv_filename
, strerror(errno
));
mdp
->rcv_mmap_addr
= (char *) addr
;
mdp
->rcv_mmap_size
= mmap_file_size
;
#define MMAP_PKT_OFFSET SWITCH_PKT_HDR_SIZE
ipc_mmap_read_pkt(int fd
, char *pkt
, int maxlen
, swtchdr
*hdr
, int timout
)
fprintf(stderr
, "INFO: ipc_mmap_read_pkt called with timout %d \n", timout
);
if ((fd
- FIRST_MMAP_FD
) >= MMAP_DATA_TABLE_SIZE
)
mdp
= &mmap_data_table
[fd
- FIRST_MMAP_FD
];
hptr
= (swtchdr
*) (mdp
->rcv_mmap_addr
+ mdp
->rcv_mmap_index
);
if (pktlen
== MMAP_CLOSE
) {
hdr
->pkt_type
= ntohl(hptr
->pkt_type
);
fprintf(stderr
, "ERROR: libswitch: ipc_mmap_read_pkt: pktlen %d > maxlen %d \n", pktlen
, maxlen
);
src
= (mdp
->rcv_mmap_addr
+ mdp
->rcv_mmap_index
) + MMAP_PKT_OFFSET
;
mdp
->rcv_mmap_index
+= MAX_SWITCH_PKT_SIZE
;
if ((mdp
->rcv_mmap_index
+ MAX_SWITCH_PKT_SIZE
) > mdp
->rcv_mmap_size
) {
ipc_mmap_write_pkt(int fd
, char *pkt
, int pktlen
, swtchdr
*hdr
)
if (pktlen
> SWITCH_ETHERMTU
) {
fprintf(stderr
, "ERROR: libswitch: ipc_mmap_write_pkt: pktlen %d > maxlen %d \n", pktlen
, SWITCH_ETHERMTU
);
if ((fd
- FIRST_MMAP_FD
) >= MMAP_DATA_TABLE_SIZE
)
mdp
= &mmap_data_table
[fd
- FIRST_MMAP_FD
];
hptr
= (swtchdr
*) (mdp
->snd_mmap_addr
+ mdp
->snd_mmap_index
);
hptr
->pkt_type
= htonl(PKT_DATA
);
hptr
->pkt_type
= htonl(hdr
->pkt_type
);
dst
= (mdp
->snd_mmap_addr
+ mdp
->snd_mmap_index
) + MMAP_PKT_OFFSET
;
mdp
->snd_mmap_index
+= MAX_SWITCH_PKT_SIZE
;
if ((mdp
->snd_mmap_index
+ MAX_SWITCH_PKT_SIZE
) > mdp
->snd_mmap_size
) {
hptr
->pkt_len
= htonl(pktlen
);
////////////////////////////////////////////////////////////////////////////////
// The following section is for Snoop-based blaze networking... //
////////////////////////////////////////////////////////////////////////////////
/*#include <dlpi.h> is up above */
* 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
/* 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
);
* 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 nic0
, nic1
, minor0
, minor1
;
fprintf(stderr
, "ERROR: No argument specified to -N \n");
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) ) {
fprintf(stderr
, "ERROR: Bad argument (%s) to -N \n", arg
);
fprintf(stderr
, "ERROR: Bad argument (%s) to -N. <digit>=<string><digit> expected. \n", arg
);
fprintf(stderr
, "ERROR: Bad argument (%s) to -N. device minor number incorrectly specified.\n", arg
);
fprintf(stderr
, "ERROR: Bad argument (%s) to -N \n", arg
);
fprintf(stdout
, "WARNING: Processing -N argument: comma expected between multiple snoop device specification.\n");
fprintf(stderr
, "ERROR: Bad GE NIC number ( %d ) specified. allowed range 0 to %d \n", nic0
, (NUM_SIMGE
-1));
fprintf(stderr
, "ERROR: Bad GE NIC number ( %d ) specified. allowed range 0 to %d \n", nic1
, (NUM_SIMGE
-1));
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
);
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
);
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
);
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
);
* 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
)
if ((fd
= open(name
, O_RDWR
)) < 0) {
printf("FYI: Unable to snoop \"%s\" (try runing as root?)\n", name
);
if (snoop_dlattachreq(fd
, minor
) < 0) goto nogood
;
if (snoop_dlokack(fd
, buf
) < 0) goto nogood
;
/* pick up everything on the wire */
if (snoop_dlpromisconreq(fd
, DL_PROMISC_PHYS
) < 0) goto nogood
;
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");
/* Flush the read side of the Stream */
if (ioctl(fd
, I_FLUSH
, FLUSHR
) < 0) {
perror("init_snoop_device: I_FLUSH");
/* 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");
*** this needs to be
saved (for use by getmsg
) in the info
[fd
] array
***
host_ipaddr
= *(uint_t
*) hp
->h_addr
;
int snoop_getmsg (int fd
, char * buf
, int maxlen
)
int snoop_putmsg (int fd
, char * buf
, int len
)
snoop_dlattachreq(int fd
, ulong_t ppa
)
dl_attach_req_t attach_req
;
attach_req
.dl_primitive
= DL_ATTACH_REQ
;
ctl
.len
= sizeof(attach_req
);
ctl
.buf
= (char *) &attach_req
;
if (putmsg(fd
, &ctl
, (struct strbuf
*) NULL
, flags
) < 0) {
perror("dlattachreq: putmsg");
snoop_dlokack(int fd
, char *bufp
)
union DL_primitives
*dlp
;
if (snoop_strgetmsg(fd
, &ctl
, (struct strbuf
*)NULL
, &flags
, "dlokack")
dlp
= (union DL_primitives
*) ctl
.buf
;
if (snoop_expecting(DL_OK_ACK
, dlp
) < 0)
if (ctl
.len
< sizeof(dl_ok_ack_t
)) {
vargs_error(0, "snoop_dlokack: response len too short: %d", ctl
.len
);
vargs_error(0, "snoop_dlokack: DL_OK_ACK was not M_PCPROTO");
snoop_dlpromisconreq(int fd
, ulong_t level
)
dl_promiscon_req_t promiscon_req
;
promiscon_req
.dl_primitive
= DL_PROMISCON_REQ
;
promiscon_req
.dl_level
= level
;
ctl
.len
= sizeof(promiscon_req
);
ctl
.buf
= (char *) &promiscon_req
;
if (putmsg(fd
, &ctl
, (struct strbuf
*) NULL
, flags
) < 0) {
vargs_error(0, "snoop_dlpromisconreq: putmsg");
snoop_dlpromiscoff(int fd
, ulong_t level
)
dl_promiscoff_req_t promiscoff_req
;
promiscoff_req
.dl_primitive
= DL_PROMISCOFF_REQ
;
promiscoff_req
.dl_level
= level
;
ctl
.len
= sizeof (promiscoff_req
);
ctl
.buf
= (char *) &promiscoff_req
;
if (putmsg(fd
, &ctl
, (struct strbuf
*) NULL
, flags
) < 0)
vargs_error(0, "dlpromiscoff: putmsg");
bind_req
.dl_primitive
= DL_BIND_REQ
;
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
.len
= sizeof (bind_req
);
ctl
.buf
= (char *) &bind_req
;
if (putmsg(fd
, &ctl
, (struct strbuf
*) NULL
, flags
) < 0) {
perror("snoop_dlbindreq: putmsg");
snoop_dlbindack(int fd
, char *bufp
)
union DL_primitives
*dlp
;
if (snoop_strgetmsg(fd
, &ctl
, (struct strbuf
*)NULL
, &flags
, "dlbindack")
dlp
= (union DL_primitives
*) ctl
.buf
;
if (snoop_expecting(DL_BIND_ACK
, dlp
) < 0)
vargs_error(0, "dlbindack: DL_OK_ACK was not M_PCPROTO");
if (ctl
.len
< sizeof (dl_bind_ack_t
)) {
vargs_error(0, "dlbindack: short response ctl.len: %d", ctl
.len
);
snoop_strioctl(int fd
, int cmd
, int timout
, int len
, char *dp
)
rc
= ioctl(fd
, I_STR
, &sioc
);
static void snoop_sigalrm (int);
(void) printf("snoop_sigalrm (%d) : TIMEOUT", i
);
(void) signal(SIGALRM
, snoop_sigalrm
);
if (alarm(MAXWAIT
) < 0) {
(void) snprintf(errmsg
, 80, "%s: alarm", caller
);
* Set flags argument and issue getmsg().
if ((rc
= getmsg(fd
, ctlp
, datap
, flagsp
)) < 0) {
(void) snprintf(errmsg
, 80, "%s: getmsg", caller
);
(void) snprintf(errmsg
, 80, "%s: alarm", caller
);
* Check for MOREDATA and/or MORECTL.
if ((rc
& (MORECTL
| MOREDATA
)) == (MORECTL
| MOREDATA
)) {
vargs_error(0, "%s: MORECTL|MOREDATA", caller
);
vargs_error(0, "%s: MORECTL", caller
);
vargs_error(0, "%s: MOREDATA", caller
);
* 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
);
snoop_dlprim(ulong_t prim
)
#define CASERET(s) case s: return (#s)
CASERET(DL_SUBS_BIND_REQ
);
CASERET(DL_SUBS_BIND_ACK
);
CASERET(DL_UNITDATA_REQ
);
CASERET(DL_UNITDATA_IND
);
CASERET(DL_DISCONNECT_REQ
);
CASERET(DL_DISCONNECT_IND
);
fprintf(stderr
, "snoop_dlprim: unknown primitive 0x%x", prim
);
return "unknown primitive";
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
));
vargs_error(int thresh
, const char *fmt
, ...)
if (netsim_debug
>= thresh
) {
vfprintf(stderr
, fmt
, ap
);
(void) fprintf(stderr
, "\n");
/* 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...
ether_chksum(char *buf
, int offset
, int len
)
shortP
= (ushort_t
*) (buf
+ offset
);
if (((unsigned long ) shortP
) & 0x1) {
fprintf(stderr
, "ERROR: ether_cksum odd address. src %p \n", shortP
);
for (index
=offset
; index
<(count
+offset
); index
+= sizeof(ushort_t
))
csum
= (csum
& 0xffff) + (csum
>> 16);
csum
= csum
& 0x0000FFFF;
// a little bit simpler version!
ether_chksum(char *buf
, int offset
, int len
)
ushort_t
*p
= (ushort_t
*) (buf
+ offset
);
// add up all the whole double-bytes...
// and if there is an odd byte at the end...
csum
+= (*((unsigned char *)p
)) & 0xff00; // big-endian !!!
// now wrap around the checksum overflow
csum
= (csum
& 0xffff) + (csum
>> 16);
// finish by taking the 1's complement
csum
= (~csum
) & 0x0000ffff;
// TCP checksum including the "pseudo header" of fields from ip header
int tcp_chksum (ethpacket_t
* pkt
)
ushort_t
*p
= (ushort_t
*) pkt
;
int bytes
= ETHHDRSZ
+ pkt
->u
.ip
.iplen
; // Total pkt-len Incl ether hdr.
csum
+= (pkt
->u
.ip
.iplen
- 4*(pkt
->u
.ip
.verlen
& 0x0f));
csum
+= p
[13] + p
[14] + p
[15] + p
[16];
// now the TCP header and data
for (i
=17; i
<(bytes
/2); i
++)
// and if there is an odd byte at the end...
// now wrap the checksum overflow back around
csum
= (csum
& 0xffff) + (csum
>> 16);
// finish by taking the 1's complement
csum
= (~csum
) & 0x0000ffff;
// compute the raw checksum of just the tcp pseudo header
int tcp_pseudocs (ethpacket_t
* pkt
)
ushort_t
*p
= (ushort_t
*) pkt
;
int bytes
= ETHHDRSZ
+ pkt
->u
.ip
.iplen
; // Total pkt-len Incl ether hdr.
csum
+= (pkt
->u
.ip
.iplen
- 4*(pkt
->u
.ip
.verlen
& 0x0f));
csum
+= p
[13] + p
[14] + p
[15] + p
[16];
// now wrap the checksum overflow back around
csum
= (csum
& 0xffff) + (csum
>> 16);
// raw checksum, no 1's complement of the result here...
ether_reverse_hword(uint16_t value
)
reverse_buf
[0] = value
& 0x00FF;
reverse_buf
[1] = (value
>> 8) & 0x00FF;
return *(uint16_t *) reverse_buf
;
ether_reverse_word(uint32_t value
)
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
;
ether_reverse_lword(uint64_t value
)
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
;
// ---------------------------------------------------------------------------
// debug levels: 0= nada; 1= decoded; 2= raw hex
netsim_snoop (char * buf
, int buflen
, int dbglevel
)
if (dbglevel
>= 1) { // -------------------------------
ethpacket_t
* packet
= (ethpacket_t
*) buf
;
fprintf (stderr
, "eth/%d{mac:%02x->%02x, ", buflen
,
eth_srcMAC(packet
) & 0xff,
eth_dstMAC(packet
) & 0xff);
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);
if (eth_tcpzlenack(packet
)) { /* happens a lot */
fprintf(stderr
, "-ack(%s)\n",
eth_tcp_string(eth_srcPORT(packet
)));
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*/)
if (packet
->u
.ip
.u
.tcp
.flags
& 0x0001/*TCP_FIN_FLAG*/)
if ((cs
= tcp_chksum (packet
)) != 0)
fprintf(stderr
," ***chksum error 0x%04x*** ", cs
);
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
);
fprintf(stderr
," %s", "ip-encaps");/*ip-encapsulation*/
fprintf(stderr
," %s", eth_icmp_string (
eth_icmptype (packet
), eth_icmpcode (packet
)));
fprintf(stderr
," %s", "igmp");/*gateway-management-prot*/
fprintf(stderr
," %s", "ggp");/*gateway-to-gateway-prot*/
fprintf(stderr
," %s", "ext-gateway");/*exterior-gw-prot*/
fprintf(stderr
," %s", "int-gateway");/*interior-gw-prot*/
default: { // unknown ip-header-protocol field
fprintf (stderr
, " %d", ipp
);
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]);
fprintf (stderr
, "arp ???");
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]);
fprintf (stderr
, "rarp ???");
default: { // unknown ether-header-type field
fprintf (stderr
, " %d", et
);
fprintf(stderr
,"------ length of buf/%d != pkt/%d \n", buflen
, pktlen
);
if (dbglevel
>= 3) { // --------------------------------------
for (i
=0; i
<pktlen
; i
+=16) {
fprintf(stderr
, "%04x :",i
);
int c
= *(unsigned char *)&(buf
[i
+j
]);
fprintf(stderr
, " %02x",c
);
out
[j
] = c
>=32 && c
<127 ? c
: '.';
for (; j
<16; j
++) fprintf(stderr
, " ..");
fprintf(stderr
, "\t: %s\n",out
);
if (dbglevel
>= 4) { // --------------------------------------
// these aren't appropriate for inlining, but they have to go somewhere...
const char * eth_tcp_string (int 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";
sprintf(&buf
[0], "%d", tcptype
);
const char * eth_icmp_string (int icmptype
, int icmpcode
)
case ICMP_ECHOREPLY
: return "echo_reply";
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 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";
sprintf(&buf
[0], "%d", icmptype
);