This is eni.c in view mode; [Download] [Up]
#include <stdio.h> #include <kernserv/kern_server_types.h> #include <net/netif.h> #include <sys/errno.h> #include <sys/types.h> #include <netinet/in.h> #include <strings.h> #include "eni.h" #include "libeni.h" #include "messages.h" struct packet_buffer { int in_use ; struct m_packet rpck ; } ; extern int curipl(void) ; extern int input(netif_t mnetif, netif_t rnetif, netbuf_t packet, void *extra) ; extern int netbuf2packet(netbuf_t nb, unsigned char *packet) ; extern int output_packet(msg_header_t *msg) ; extern kern_return_t thread_resume(thread_t t) ; extern kern_return_t thread_suspend(thread_t t) ; extern port_t thread_self(void) ; extern void queue_packet(netbuf_t p) ; extern void send_pmsg(void *ignore) ; kern_server_t instance ; static netif_t dev_netif ; /* netif_t of the device driver */ static port_t in_port ; /* send input packets here */ static struct eni_stats global_stats ; static struct m_reply_ret_code msg_rcode ; #if 0 static struct m_reply_stats msg_stats ; #endif static struct packet_buffer pbuf ; static u_short eni_frame_type ; /* Tell the device driver we want its packets. */ static void attach(void *private, netif_t rnetif) { netif_t mnetif ; if(strcmp(if_type(rnetif), IFTYPE_ETHERNET) != 0) { return ; } mnetif = if_attach(0, input, 0, 0, 0, ENI_NAME, ENI_UNIT, ENI_TYPE_NAME, ENI_MTU, IFF_UP, NETIFCLASS_VIRTUAL, (void *)0) ; if(mnetif == 0) { printf("%s: attach: error from if_attach()\n", ENI_NAME) ; return ; } dev_netif = rnetif ; } /* This gets called when we are loaded by the kernel server loader. */ void config(int n) { eni_frame_type = htons(ENI_FRAME_TYPE) ; if_registervirtual(attach, 0) ; } /* This routine handles all the messages we receive. It is called automatically when a message arrives. If 'r' is 0 return TRUE, otherwise return FALSE. */ boolean_t handle_msg(msg_header_t *msg, void *extra) { int r = 0 ; int send_reply = 0 ; msg_return_t m ; void *reply = (void *)0 ; /* kludge */ switch(msg->msg_id) { /* We've received a packet to be output. Send it to the device driver, then send a reply back containing the return code. */ case MSG_SEND_PACKET: r = output_packet(msg) ; if(msg->msg_remote_port != PORT_NULL) { send_reply = 1 ; reply = (void *)&msg_rcode ; msg_rcode.head.msg_simple = TRUE ; msg_rcode.head.msg_size = sizeof(struct m_reply_ret_code) ; msg_rcode.head.msg_type = MSG_TYPE_NORMAL ; msg_rcode.head.msg_local_port = PORT_NULL ; msg_rcode.head.msg_remote_port = msg->msg_remote_port ; msg_rcode.head.msg_id = MSG_RET_CODE ; msg_rcode.type.msg_type_name = MSG_TYPE_INTEGER_32 ; msg_rcode.type.msg_type_size = sizeof msg_rcode.ret_code * 8 ; msg_rcode.type.msg_type_number = 1 ; msg_rcode.type.msg_type_inline = TRUE ; msg_rcode.type.msg_type_longform = FALSE ; msg_rcode.type.msg_type_deallocate = FALSE ; msg_rcode.ret_code = r ; } break ; /* We've received a request for statistics. Unfinished. */ #if 0 case MSG_STATS: if(msg->msg_remote_port != PORT_NULL) { send_reply = 1 ; reply = (void *)&msg_stats ; msg_stats.head.msg_simple = TRUE ; msg_stats.head.msg_size = sizeof(struct m_reply_stats) ; msg_stats.head.msg_type = MSG_TYPE_NORMAL ; msg_stats.head.msg_local_port = PORT_NULL ; msg_stats.head.msg_remote_port = msg->msg_remote_port ; msg_stats.head.msg_id = MSG_STATS ; bcopy((void *)&global_stats, (void *)&msg_stats.stats, sizeof(struct m_reply_stats)) ; } break ; #endif /* The client is giving us access to the port on which it will receive packets. */ case MSG_INIT: #ifdef DEBUG printf("%s: handle_msg: MSG_INIT.\n", ENI_NAME) ; #endif send_reply = 0 ; in_port = msg->msg_remote_port ; break ; default: r = 1 ; printf("%s: handle_msg: unknown message type: %d\n", ENI_NAME, msg->msg_id) ; break ; } /* Send a reply, if one is expected. */ if(send_reply) { m = msg_send((msg_header_t *)reply, MSG_OPTION_NONE, 0) ; if(m != SEND_SUCCESS) { printf("%s: handle_msg: msg_send() returns %d\n", ENI_NAME, m) ; } else { global_stats.replies++ ; } } return r == 0 ? TRUE : FALSE ; } /* Receive a packet from the device driver. If the frame type is ours then have it sent to the client. Return 0 on success. */ int input(netif_t mnetif, netif_t rnetif, netbuf_t pck_nb, void *extra) { int r ; u_short etype ; #ifdef DEBUG printf("%s: input: start.\n", ENI_NAME) ; printf("%s: input: full packet size = %d.\n", ENI_NAME, (int)nb_size(pck_nb)) ; #endif r = nb_read(pck_nb, (unsigned)TYPE_OFFSET, (unsigned)TYPE_SIZE, (void *)&etype) ; if(r) { return r ; } else { if(etype != ENI_FRAME_TYPE) { return EAFNOSUPPORT ; } else { global_stats.in_packets++ ; queue_packet(pck_nb) ; return 0 ; } } } /* Return 0 on success, positive integer on failure. *** Look through errno.h for more appropriate return codes. *** Cause a packet of the form: daddr:saddr:type:utype:...data... to be sent. */ int output_packet(msg_header_t *msg) { int dlen ; /* packet data length */ int r ; netbuf_t onb ; /* output netbuf_t (wrapper) */ u_short user_frame_type ; /* the type of the frame given to us */ unsigned char *pck ; /* pointer to packet */ unsigned char *daddr ; /* destination Ethernet address */ #ifdef DEBUG printf("%s: output_packet: start.\n", ENI_NAME) ; #endif onb = if_getbuf(dev_netif) ; if(onb == (netbuf_t)0) { printf("%s: output_packet: can't get netbuf.\n", ENI_NAME) ; return ENOBUFS ; } pck = ((struct m_packet *)msg)->data ; dlen = ((struct m_packet *)msg)->eh.data_length ; daddr = ((struct m_packet *)msg)->eh.daddr ; user_frame_type = ((struct m_packet *)msg)->eh.frame_type ; /* Put our type into the frame type field of the packet, the user specified type into the first two bytes of the data section, followed by the user's data. Check return codes. */ if(nb_write(onb, (unsigned)TYPE_OFFSET, (unsigned)TYPE_SIZE, (void *)&eni_frame_type) || nb_write(onb, (unsigned)DATA_OFFSET, (unsigned)TYPE_SIZE, (void *)&user_frame_type) || nb_write(onb, (unsigned)(DATA_OFFSET + TYPE_SIZE), (unsigned)dlen, (void *)pck)) { printf("%s: output_packet: nb_write() failure.\n", ENI_NAME) ; return ENOBUFS ; } if(nb_shrink_bot(onb, (unsigned)(nb_size(onb) - ETHERHDRSIZE - dlen - TYPE_SIZE))) { printf("%s: output_packet: nb_shrink_bot() failed.\n", ENI_NAME) ; return ENOBUFS ; } /* Fire it off... */ #ifdef DEBUG printf("%s: output_packet: full packet size = %d.\n", ENI_NAME, (int)nb_size(onb)) ; #endif r = if_output(dev_netif, onb, (void *)daddr) ; if(r != 0) { if(r == ENXIO) /* so what else, exactly, can it return?! */ { printf("%s: output_packet: if_output(): output not implemented.\n", ENI_NAME) ; return ENODEV ; } else { printf("%s: output_packet: if_output() returned %d.\n", ENI_NAME, r) ; return EIO ; } } return 0 ; } /* Just stuff the packet in a message and let the front end library routines worry about swapping in the user frame type. */ void queue_packet(netbuf_t nb) { int psize ; kern_return_t r ; #ifdef DEBUG printf("%s: queue_packet: start.\n", ENI_NAME) ; #endif /* If the packet is in the process of being sent to the user, drop the current one. We don't want to trash the packet being sent. */ if(pbuf.in_use) { #ifdef DEBUG printf("%s: queue_packet: packet buffer in use -- dropped.\n", ENI_NAME) ; #endif return ; } /* If the buffer is not currently in use then we are assured that it will not be accessed until this routine returns, since it is called from the device driver. */ pbuf.in_use = 1 ; /* Move the data out of the netbuf_t and into the data area of the message. */ psize = nb_size(nb) ; if(nb_read(nb, (unsigned)0, (unsigned)psize, (void *)pbuf.rpck.data)) { printf("%s: queue_packet: error moving netbuf_t to message.\n", ENI_NAME) ; global_stats.lost_out_packets++ ; return ; } pbuf.rpck.head.msg_simple = TRUE ; pbuf.rpck.head.msg_size = sizeof(struct m_packet) ; pbuf.rpck.head.msg_type = MSG_TYPE_NORMAL ; pbuf.rpck.head.msg_local_port = PORT_NULL ; pbuf.rpck.head.msg_remote_port = in_port ; pbuf.rpck.head.msg_id = MSG_HAVE_PACKET ; pbuf.rpck.type_1.msg_type_name = MSG_TYPE_BYTE ; /* ??? */ pbuf.rpck.type_1.msg_type_size = sizeof pbuf.rpck.eh * 8 ; pbuf.rpck.type_1.msg_type_number = 1 ; pbuf.rpck.type_1.msg_type_inline = TRUE ; pbuf.rpck.type_1.msg_type_longform = FALSE ; pbuf.rpck.type_1.msg_type_deallocate = FALSE ; pbuf.rpck.eh.data_length = psize - ETHERHDRSIZE - TYPE_SIZE ; pbuf.rpck.type_2.msg_type_name = MSG_TYPE_BYTE ; pbuf.rpck.type_2.msg_type_size = 8 ; pbuf.rpck.type_2.msg_type_number = sizeof pbuf.rpck.data ; pbuf.rpck.type_2.msg_type_inline = TRUE ; pbuf.rpck.type_2.msg_type_longform = FALSE ; pbuf.rpck.type_2.msg_type_deallocate = FALSE ; /* Schedule 'send_pmsg' to be run, which will send the packet to the user via a message. 'kern_serv_callout' does not sleep/block. */ if((r = kern_serv_callout(&instance, send_pmsg, (void *)0)) != KERN_SUCCESS) { printf("%s: queue_packet: kern_serv_callout() failed with %d.\n", ENI_NAME, (int)r) ; } } /* Send the message containing the packet to the packet reception port of the front-end library. */ void send_pmsg(void *ignore) { msg_return_t m ; #ifdef DEBUG printf("%s: send_pmsg: start.\n", ENI_NAME) ; #endif if( ! pbuf.in_use) { printf("%s: send_pmsg: BUG: packet buffer not in use.\n", ENI_NAME) ; } else { #ifdef DEBUG printf("%s: send_pmsg: data length = %d.\n", ENI_NAME, pbuf.rpck.eh.data_length) ; #endif m = msg_send((msg_header_t *)&pbuf.rpck, MSG_OPTION_NONE, 0) ; if(m != SEND_SUCCESS) { global_stats.lost_out_packets++ ; } /* Free it so we can receive more packets. */ pbuf.in_use = 0 ; #ifdef DEBUG printf("%s: send_msg: msg_send() returned %d.\n", ENI_NAME, m) ; #endif } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.