This is libeni.c in view mode; [Download] [Up]
/*
User level interface to the Ethernet.
These routines constitute a library, that can be linked with user
programs, to provide low level access to the Ethernet.
- Bill Heelan (wheelan@cs.mcgill.ca)
*/
#include <stdio.h>
#include <stdlib.h>
#include <mach.h>
#include <servers/netname.h>
#include <sys/message.h>
#include <net/etherdefs.h>
#include <errno.h>
#include "eni.h"
#include "libeni.h"
#include "messages.h"
#define ENI_PORT_NAME "eni0"
extern int bcopy(char *src, char *dst, int len) ;
kern_return_t eni_errno = KERN_SUCCESS ;
static port_t eni_port ; /* send packets and status requests */
static port_t rc_port ; /* receive return codes and status info here */
static port_t rpck_port ; /* receive packets on this port */
static int init_success = 0 ;
/*
Do necessary initializations. Set up a separate message port to receive
packets.
Return 0 on success, -1 on failure. In the case of failure set the
variable 'eni_errno' to the value of the last kernel return code.
*/
int eni_init(void)
{
kern_return_t r ;
msg_return_t m ;
struct m_init init_m ;
eni_errno = 0 ;
if(init_success)
{
eni_errno = EAGAIN ;
return -1 ;
}
r = netname_look_up(name_server_port, "", ENI_PORT_NAME, &eni_port) ;
if(r != NETNAME_SUCCESS)
{
eni_errno = ENXIO ;
return -1 ;
}
r = port_allocate(task_self(), &rc_port) ;
if(r != KERN_SUCCESS)
{
eni_errno = ENOBUFS ;
return -1 ;
}
r = port_allocate(task_self(), &rpck_port) ;
if(r != KERN_SUCCESS)
{
eni_errno = ENOBUFS ;
return -1 ;
}
/*
Give the kernel module access to the port on which we want to
receive packets.
*/
init_m.head.msg_simple = TRUE ;
init_m.head.msg_size = sizeof init_m ;
init_m.head.msg_type = MSG_TYPE_NORMAL ;
init_m.head.msg_local_port = rpck_port ; /* receive packets here */
init_m.head.msg_remote_port = eni_port ;
init_m.head.msg_id = MSG_INIT ;
if((m = msg_send((msg_header_t *)&init_m, MSG_OPTION_NONE, 0)) != SEND_SUCCESS)
{
eni_errno = ENOINIT ;
return -1 ;
}
else
{
init_success = 1 ;
return 0 ;
}
}
/*
Wait for a packet to be put on our receive queue.
Return 0 on success, -1 if an error occurs.
We must do a bit of massaging, since the packet in the message we get
will have the format:
daddr:saddr:type:utype:...data...
while the user should see:
daddr:saddr:utype:...data...
(type is the frame type we have reserved, and utype is the frame type
specified by the user.)
*/
int eni_get_packet(unsigned char *packet, int *dlen)
{
msg_return_t m ;
struct m_packet rpck_m ;
eni_errno = 0 ;
if( ! init_success)
{
eni_errno = ENOINIT ;
return -1 ;
}
rpck_m.head.msg_size = sizeof rpck_m ;
rpck_m.head.msg_local_port = rpck_port ;
if((m = msg_receive((msg_header_t *)&rpck_m, MSG_OPTION_NONE, 0)) != RCV_SUCCESS)
{
eni_errno = ENXIO ;
return -1 ;
}
else
{
*dlen = rpck_m.eh.data_length ;
#ifdef DEBUG
fprintf(stderr, "eni_get_packet: got packet with data length %d.\n",
*dlen) ;
#endif
bcopy((char *)rpck_m.data, (char *)packet, 2 * ADDR_SIZE) ;
bcopy((char *)(rpck_m.data + DATA_OFFSET), (char *)(packet + TYPE_OFFSET),
*dlen + TYPE_SIZE) ;
return 0 ;
}
}
/*
Send an Ethernet packet to the machine whose hardware Ethernet address
is given by 'dst_addr'. 'frame_type' is the value to be put into the
type field of the packet, while 'data' and 'dlen' are a pointer to the
packet data and the length of this data, respectively.
Both 'dst_addr' and 'frame_type' are expected to already be in network
byte order.
Return 0 on sucess, -1 otherwise.
*/
int eni_send_packet(unsigned char *dst_addr, u_short frame_type, void *data,
int dlen)
{
msg_return_t m ;
struct m_reply_ret_code rep_m ;
struct m_packet pck_m ;
eni_errno = KERN_SUCCESS ;
if( ! init_success)
{
eni_errno = ENOINIT ;
return -1 ;
}
if(dlen > ENI_MTU)
{
eni_errno = E2BIG ;
return -1 ;
}
pck_m.head.msg_simple = TRUE ;
pck_m.head.msg_size = sizeof pck_m ;
pck_m.head.msg_type = MSG_TYPE_NORMAL ;
pck_m.head.msg_local_port = rc_port ; /* return code port */
pck_m.head.msg_remote_port = eni_port ;
pck_m.head.msg_id = MSG_SEND_PACKET ;
pck_m.type_1.msg_type_name = MSG_TYPE_BYTE ; /* ??? */
pck_m.type_1.msg_type_size = sizeof pck_m.eh * 8 ;
pck_m.type_1.msg_type_number = 1 ;
pck_m.type_1.msg_type_inline = TRUE ;
pck_m.type_1.msg_type_longform = FALSE ;
pck_m.type_1.msg_type_deallocate = FALSE ;
pck_m.eh.data_length = dlen ;
pck_m.eh.frame_type = frame_type ;
bcopy((char *)dst_addr, (char *)&pck_m.eh.daddr, ADDR_SIZE) ;
pck_m.type_2.msg_type_name = MSG_TYPE_BYTE ;
pck_m.type_2.msg_type_size = 8 ;
pck_m.type_2.msg_type_number = ENI_MTU ; /* change to dlen+header+...? */
pck_m.type_2.msg_type_inline = TRUE ;
pck_m.type_2.msg_type_longform = FALSE ;
pck_m.type_2.msg_type_deallocate = FALSE ;
bcopy((char *)data, (char *)&pck_m.data[0], dlen) ;
#ifdef DEBUG
fprintf(stderr, "eni_send_packet: sending packet with data length %d.\n",
dlen) ;
#endif
if((m = msg_send((msg_header_t *)&pck_m, MSG_OPTION_NONE, 0)) != SEND_SUCCESS)
{
eni_errno = ENXIO ;
return -1 ;
}
rep_m.head.msg_size = sizeof rep_m ;
rep_m.head.msg_local_port = rc_port ;
if((m = msg_receive((msg_header_t *)&rep_m, MSG_OPTION_NONE, 0)) != RCV_SUCCESS)
{
return 0 ; /* We have to hope... */
}
else
{
return (eni_errno = rep_m.ret_code) == 0 ? 0 : -1 ;
}
}
#if 0
/*
Get statistics on the number of packet sent, received and errors.
UNFINISHED.
*/
int eni_stats(struct eni_stats *s)
{
msg_return_t m ;
struct m_reply_stats sr_m ;
sr_m.head.msg_simple = TRUE ;
sr_m.head.msg_size = sizeof sr_m ;
sr_m.head.msg_type = MSG_TYPE_NORMAL ;
sr_m.head.msg_local_port = rc_port ; /* return code port */
sr_m.head.msg_remote_port = eni_port ;
sr_m.head.msg_id = MSG_STATS ;
sr_m.type.msg_type_name = MSG_TYPE_BYTE ; /* ??? */
sr_m.type.msg_type_size = sizeof(struct eni_stats) * 8 ;
sr_m.type.msg_type_number = 1 ;
sr_m.type.msg_type_inline = TRUE ;
sr_m.type.msg_type_longform = FALSE ;
sr_m.type.msg_type_deallocate = FALSE ;
m = msg_send((msg_header_t *)&sr_m, MSG_OPTION_NONE, 0) ;
if(m != SEND_SUCCESS)
{
eni_errno = m ;
return -1 ;
}
return 0 ;
}
#endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.