www.pudn.com > test11.rar > plugs.c, change:2008-07-31,size:85842b
// file: plugs.c
//
// Implementation of the plugs
// tcp/ip stack for embedded systems
//
// Author: dvb, 2001-2002
//
// ex:set tabstop=4:
// ex:set expandtab:
// ex:set shiftwidth=4:
#include "plugs.h"
#include "plugs_private.h"
// +-----------------
// | The Globals
s_plugs_globals ng_plugs_globals;
// +------------------------
// | Debug bit 4 means show debug on system plugs
// |
#if (PLUGS_DEBUG & 4)
#define nk_plugs_default_debug (ne_plugs_flag_debug_tx | ne_plugs_flag_debug_rx)
#else
#define nk_plugs_default_debug (0)
#endif
// +------------------------
// | Local Prototypes
static void r_add_to_arp_cache_from_ip_packet
(
s_plugs_adapter_entry *a,
ns_plugs_ethernet_packet *p
);
static void r_add_to_arp_cache
(
s_plugs_adapter_entry *a,
net_32 ip_address,
net_32 ethu32,net_16 ethl16
);
static int r_plugs_dns_proc
(
int plug_handle,
void *context,
ns_plugs_packet *p,
void *payload,
int payload_length
);
static int r_tcp_dispatch
(
s_plugs_adapter_entry *a,
int plug_handle,
s_plugs_plug *plug,
ns_plugs_packet *sub_protocol,
void *payload,
int payload_length
);
static int r_tcp_send
(
int plug_handle,
s_plugs_plug *plug,
int flags,
void *payload,
long payload_length
);
void r_tcp_plug_set_inert (s_plugs_plug* plug);
static int wellspring_of_sequence_numbers(void)
{
static int seed;
seed += 70000013;
seed += nr_timer_milliseconds() * 1000219;
return seed;
}
static s_plugs_plug *r_plug_from_handle(int plug_handle);
static void r_plugs_irq_handler(int context,int irq_number, int
interruptee_pc);
static void r_set_irqs_for_plug (s_plugs_plug* plug, int on_off);
// ---------------------------
// Byte-swapping subroutines
host_16 nr_n2h16(net_16 n)
{
return nm_n2h16(n);
}
host_32 nr_n2h32(net_32 n)
{
return nm_n2h32(n);
}
static s_plugs_plug *r_plug_from_handle(int plug_handle)
{
s_plugs_plug *plug;
s_plugs_globals *g = &ng_plugs_globals;
plug_handle -= nk_plugs_plug_number_base;
plug = &g->plug[plug_handle];
return plug;
}
// --------------------
// r_calculate_checksum(base,byte_count,ip_header)
//
// Return complement of 1's-complement
// checksum of the specified range
// Return in network byte order.
//
// If ip_header pointer is included, add in
// the pseudo header as used by udp & tcp
// And possibly add in the (host-ordered)
// pseudo_length, too.
//
net_16 r_calculate_checksum
(
void *base,
int byte_count,
ns_plugs_ip_packet *ip_packet,
host_16 pseudo_length
)
{
net_16 *w = base;
host_32 x;
unsigned long result = 0;
while(byte_count > 0)
{
x = nr_n2h16(*w++);
if(byte_count == 1)
x &= 0xff00;
result += x;
byte_count -= 2;
}
// | Add in udp/tcp pseudo-header, maybe
if(ip_packet)
{
result += nr_n2h16(ip_packet->source_ip_address & 0x0000ffff);
result += nr_n2h16(ip_packet->source_ip_address >> 16);
result += nr_n2h16(ip_packet->destination_ip_address & 0x0000ffff);
result += nr_n2h16(ip_packet->destination_ip_address >> 16);
result += ip_packet->protocol;
result += pseudo_length;
}
// | strangely, tcp/ip checksums require
// | that the carry-bit be added back in.
// | And, do it twice, to account for the case
// | where adding the halves overflows. You
// | only need to do it twice at most. Really.
// |
result = (result & 0xffff) + (result >> 16);
result = (result & 0xffff) + (result >> 16);
// |
// | Never have checksum == 0
// |
if(result == 0)
result = 0xffff;
return ~(nr_h2n16((host_16)result));
}
#if PLUGS_PING
// --------------------------------------------------------
// ICMP Handler
//
// This handler only responds to one thing: a ping (echo request)
//
#define k_plugs_ping_reply_size 1536
static int r_plugs_icmp_proc
(
int plug_handle,
void *context,
ns_plugs_packet *p,
void *payload,
int payload_length
)
{
char response_b[k_plugs_ping_reply_size]; // (raw bytes) upper bound for ping we can reply to!
ns_plugs_icmp_packet *icmpp = payload;
ns_plugs_icmp_packet *response = (ns_plugs_icmp_packet *)response_b;
if(icmpp->type == ne_plugs_icmp_ping_request)
{
char *ws,*wd;
int i;
ns_plugs_ip_packet *ipp;
// | copy payload portion of ping request
ws = payload;
ws += 4;
wd = response->payload;
if(payload_length >= k_plugs_ping_reply_size)
payload_length = k_plugs_ping_reply_size;
for(i = payload_length - 4; i > 0; i--)
*wd++ = *ws++;
response->type = ne_plugs_icmp_ping_reply;
response->code = 0;
// | Send the almost-identical packet on out
// | Use send_to to send it to the originator
ipp = p[ne_plugs_ip].header;
nr_plugs_send_to(plug_handle,response,payload_length,0,ipp->source_ip_address,0);
}
return 0;
}
#endif // PLUGS_PING
// ---------------------------------------------------------
// Arp Handler
//
// When we get an arp "reply" packet,
// we'll save its information into the
// cache IF there is an entry for it already
// there.
// When we get an arp "request" packet,
// we'll reply to it if its about us.
int r_plugs_arp_proc
(
int plug_handle,
void *context,
ns_plugs_packet *p,
void *payload,
int payload_length
)
{
ns_plugs_arp_packet *ap = payload;
ns_plugs_arp_packet response;
s_plugs_adapter_entry *a = context;
switch(ap->op)
{
case nm_h2n16(1): // arp request
if(a->settings.ip_address == ap->target_ip_address)
{
// | Request for us!
// | construct an answer and send it.
response.hardware_type = nm_h2n16(1);
response.protocol_type = nm_h2n16(0x0800);
response.hardware_size = 6; // 6 for ethernet
response.protocol_size = 4; // 4 for IP
response.op = nm_h2n16(2); // reply
response.sender_ethernet_address = a->settings.ethernet_address;
response.sender_ip_address = a->settings.ip_address;
response.target_ethernet_address = ap->sender_ethernet_address;
response.target_ip_address = ap->sender_ip_address;
nr_plugs_send
(
plug_handle,
&response,
28,
ne_plugs_flag_ethernet_broadcast
);
}
break;
case nm_h2n16(2): // arp reply
// |
// | walk through our arp cache, and see if we want this reply
// |
{
s_plugs_arp_cache_entry *w;
int i;
w = a->arp_cache;
for(i = 0; i < nk_plugs_arp_cache_count; i++)
{
if(w->ip_address == ap->sender_ip_address)
w->ethernet_address = ap->sender_ethernet_address;
w++;
}
}
break;
} // switch
return 0;
}
// Nothing wrong with two plugs listening
// on the same port. No sir! Perfectly
// legal. But we wont let it happen
// accidentally.
#define nk_plugs_minimum_fresh_port_number 32769
net_16 get_port_number(s_plugs_globals *g)
{
int v;
s_plugs_plug *plug;
int i;
try_again:
v = g->port_number++; // will be zero the very first time...
if(v == 0)
v = g->port_number = nk_plugs_minimum_fresh_port_number;
v = nr_h2n16(v);
plug = g->plug;
for(i = 0; i < PLUGS_PLUG_COUNT; i++)
if(v == (int)plug++->local_port)
goto try_again;
return v;
}
// +---------------------------------------
// | This routine takes the index of an uninitialized
// | adapter. The "network settings" have already been
// | stashed in it, but only the Ethernet address is
// | relevant for now.
// |
// | This routine will initialize the adapter (low level),
// | and perform a dhcp lookup using it.
// |
// | If successful, it will fill in the dhcp-assigned
// | network settings in the adapter's settings, and
// | return zero-or-positive.
// |
// | If unsuccessful, will return a negative value
// |
static int r_plugs_adapter_dhcp(int adapter_index)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = &g->adapter[adapter_index];
ns_plugs_network_settings ns; // for dhcp routine to fill in...
int result = 0;
// |
// | Step 1: initialize the adapter hardware
// |
result = (a->adapter_description->reset_proc)
(
a->adapter_address,
&a->adapter_storage,
&a->settings
);
if(result < 0)
goto go_home;
#if PLUGS_IRQ
if((a->adapter_irq != 0) && (a->adapter_description->set_irq_proc))
{
nr_installuserisr2(a->adapter_irq,r_plugs_irq_handler,(int)a);
result = (a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
1
);
if(result < 0)
goto go_home;
}
#endif // PLUGS_IRQ
// | Step 2: let the dhcp routine do all the hard work...
result = r_plugs_dhcp(adapter_index,&ns);
if(result < 0)
goto go_home;
// | Step 3: stash the settings received for just us
a->settings = ns;
go_home:
nr_plugs_print_error_message("[r_plugs_adapter]",result);
return result;
}
int nr_plugs_initialize
(
long flags,
ns_plugs_network_settings *network_settings,
void *adapter_address,
int adapter_irq,
ns_plugs_adapter_description *adapter_description
)
{
s_plugs_globals *g = &ng_plugs_globals;
int result = 0;
s_plugs_adapter_entry *a;
int first_adapter = !(flags & ne_plugs_flag_add_adapter);
int adapter_index;
// +---------------------------------
// | Initialize can be used to start up the plugs
// | stack from scratch, or to add one more adapter
// |
if(first_adapter)
{
// +----------------------
// | clear and reset everything
// | Only the 1st adapter gets a DNS socket
// |
nr_zerorange((char *)g,sizeof(s_plugs_globals));
// +---------------
// | Mark the globals as Up And Running
g->running = 1;
}
// +---------------------------------------
// | Allocate the adapter spot, and bump
// | the adapter_count. At the end, if there was any problem, we'll
// | decrement the adapter_count and return and error
adapter_index = g->adapter_count++;
a = &g->adapter[adapter_index];
nr_zerorange((char *)a,sizeof(s_plugs_adapter_entry));
a->adapter_address = adapter_address;
a->adapter_irq = adapter_irq;
a->adapter_description = adapter_description;
// +-----------------
// | Install this adapter's network settings.
// | If there's a flash, maybe get settings from flash
// |
#ifdef nasys_plugs_persistent_network_settings
// | 0 means use the stored settings in flash
if((int)network_settings == 0)
network_settings = &nasys_plugs_persistent_network_settings->settings;
#endif // nasys_plugs_persistent_network_settings
a->settings = *network_settings;
a->settings.flags |= flags; // use the flags passed in, too.
// +--------------------
// | Install this adapter's arp proc
// | Every adapter needs one of these, it is essential for IP
// |
// | Turns out to be ok to create the plug before the
// | possible DHCP negotiation, or hardware initialization.
result = nr_plugs_create
(
&a->arp_plug,
ne_plugs_arp,
1,
r_plugs_arp_proc,
a, // | use context to hold "our adapter".
ne_plugs_flag_ethernet_broadcast + nk_plugs_default_debug + adapter_index
);
if(result < 0)
goto go_home;
#if PLUGS_DHCP
// +--------------------------------------
// | If the dhcp flag is set (either in the network_settings
// | flags or in the plugs_initialize flags) then call
// | the dhcp proc to get network_settings.
// | (if so, the settings still needs to start with a unique
// | ethernet address; the rest is filled in by dhcp)
// |
if(a->settings.flags & ne_plugs_flag_dhcp) // includes flags parameter already
{
result = r_plugs_adapter_dhcp(adapter_index);
if(result < 0)
goto go_home;
}
#endif // PLUGS_DHCP
// +----------------------------------------
// | Initialize the adapter (possibly for a second time,
// | if a dhcp negotiation occurred...
// |
result = (a->adapter_description->reset_proc)
(
a->adapter_address,
&a->adapter_storage,
&a->settings
);
if(result < 0)
goto go_home;
#if PLUGS_IRQ
// +--------------------
// | If requested and available, install
// | the irq handler
// |
if((a->adapter_irq != 0) && (a->adapter_description->set_irq_proc))
{
// |
// | Install our handler
// |
nr_installuserisr2(a->adapter_irq,r_plugs_irq_handler,(int)a);
// |
// | Tell adapter to irq us
// |
result = (a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
1
);
if(result < 0)
goto go_home;
}
#endif // PLUGS_IRQ
#if PLUGS_DEBUG
printf(" [plugs] +-----------------------\n");
printf(" [plugs] | initialized adapter %s at 0x%08x, ",
a->adapter_description->adapter_name,
a->adapter_address);
nr_plugs_print_ip_address(a->settings.ip_address);
printf("\n");
#endif // PLUGS_DEBUG
// +----------------------------
// | For the first adapter only,
// | (conditionalized code allowing),
// | install a plug for doing DNS queries
// |
#if PLUGS_DNS
if(first_adapter)
{
// +------
// | Install a DNS handler
// |
result = nr_plugs_create
(
&g->dns_plug,
ne_plugs_udp,
0,
r_plugs_dns_proc,
0,
nk_plugs_default_debug);
if(result < 0)
goto go_home;
result = nr_plugs_connect
(
g->dns_plug,
0,
nr_n2h32(a->settings.nameserver_ip_address),
ne_plugs_udp_dns
);
if(result < 0)
goto go_home;
}
#endif // PLUGS_DNS
#if PLUGS_PING
// +------
// | Install this adapter's ICMP ping handler
// |
result = nr_plugs_create
(
&a->icmp_plug,
ne_plugs_icmp,
1,
r_plugs_icmp_proc,
0,
nk_plugs_default_debug + adapter_index
);
if(result < 0)
goto go_home;
result = nr_plugs_connect
(
a->icmp_plug,
0,
-1,
-1 // accept from anyone who sends to us
);
if(result < 0)
goto go_home;
#endif // PLUGS_PING
go_home:
if(result < 0)
{
// +-------------------------
// | If there was any problem initializing this adapter,
// | we roll it back out.
// |
// (if we had a "destroy adapter" loop, we would iterate
// through every plug and destroy any associated with this
// adapter index. but we dont, we do it explicitlike here.)
#if PLUGS_PING
if(a->icmp_plug)
{
nr_plugs_destroy(a->icmp_plug);
a->icmp_plug = 0;
}
#endif
#if PLUGS_DNS
if(first_adapter && g->dns_plug)
{
nr_plugs_destroy(g->dns_plug);
g->dns_plug = 0;
}
#endif
if(a->arp_plug)
{
nr_plugs_destroy(a->arp_plug);
a->arp_plug = 0;
}
// (reset the adapter only to disable its interrupts)
// (if the description proc ptr was bad, we crashed
// already and arent running here.)
(a->adapter_description->reset_proc)
(
a->adapter_address,
&a->adapter_storage,
&a->settings
);
g->adapter_count--;
}
return result;
}
int nr_plugs_terminate(void)
{
s_plugs_globals *g = &ng_plugs_globals;
int i;
s_plugs_adapter_entry *a;
// |
// | Turn off interrupts right now
// |
a = g->adapter;
for(i = 0; i < g->adapter_count; i++)
{
(a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
0
);
a++;
}
// |
// | Delete each plug
// |
for(i = 0; i < PLUGS_PLUG_COUNT; i++)
nr_plugs_destroy(i + nk_plugs_plug_number_base);
// |
// | and wipe the globals.
// |
nr_zerorange((char *)g,sizeof(s_plugs_globals));
return 0;
}
// +-----------------------------
// | nr_plugs_force_timeout is intended to allow a
// | user intervention for certain long synchronous
// | activities.
// |
// | dhcp lookup, for example, has a 60-second timeout,
// | which is a long time. If you get tired of waiting,
// | you can call nr_plugs_force_timeout to cut it short,
// | and the routine will return "failed".
// |
// | Has to be called from interrupt level, obviously, since
// | your foreground process is waiting for
// | nr_plugs_initialize to return.
// |
// | Presently (2003): only affects nr_plugs_initialize
// | if dhcp flag is set.
// |
int nr_plugs_force_timeout
(
void
)
{
ng_plugs_globals.force_timeout = 1;
return 0;
}
int nr_plugs_get_settings
(
int adapter_index,
ns_plugs_network_settings *network_settings_out
)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = &g->adapter[adapter_index];
*network_settings_out = a->settings;
return 0;
}
int ethernet_address_equal(net_48 *ea1,net_48 *ea2)
{
return ea1->u32 == ea2->u32
&& ea1->l16 == ea2->l16;
}
//
// Accept ip match, or filter = -1 gets everything
int ip_address_equal(net_32 packet_ip_address,net_32 filter_ip_address)
{
return (filter_ip_address == 0xffffffff) || (packet_ip_address == filter_ip_address);
}
//
// Accept ip match, or filter = -1 gets everything
int port_number_equal(net_16 packet_port,net_16 filter_port)
{
return (filter_port == 0xffff) || (packet_port == filter_port);
}
// debug routine: get_protocol_name
#if PLUGS_DEBUG
char *get_protocol_name(int protocol)
{
switch(protocol)
{
case ne_plugs_ethernet:
return "ethernet";
case ne_plugs_arp:
return "arp";
case ne_plugs_ip:
return "ip";
case ne_plugs_icmp:
return "icmp";
case ne_plugs_udp:
return "udp";
case ne_plugs_tcp_raw:
case ne_plugs_tcp:
return "tcp";
default:
return "unknown";
}
}
#endif // PLUGS_DEBUG
// nr_plugs_dispatch_packet
//
// Examine an ethernet packet and find out what protocol
// it is.
// Then loop through every plug and see which ones should
// receive this packet.
//
// This routine is called by an adapters polling proc.
//
int nr_plugs_dispatch_packet(void *pp,int packet_length,void *context)
{
ns_plugs_ethernet_packet *packet = pp; // only one link_layer_type today.
s_plugs_globals *g = &ng_plugs_globals;
//int is_protocol[ne_plugs_last_protocol];
ns_plugs_packet sub_protocol[ne_plugs_last_protocol];
int i;
s_plugs_plug *plug;
int p;
long plug_bitmask;
int got_tcp = 0; // so only one tcp plug gets a tcp packet
s_plugs_adapter_entry *a = context;
// | since most interesting packets are ip-derivative,
// | we figure out the sizes &c and put them in these vars
// |
ns_plugs_ip_packet *ip_packet = 0; // if incoming packet is ip or better...
void *past_ip_packet = 0; // if incoming packet is ip or better
int ip_payload_length = 0;
// | Not running? go home now.
// |
if(!g->running)
return ne_plugs_error_not_initialized; // | plugs not initialized
g->in_dispatch++;
// |
// | Start with no protocol set
for(i = 0; i < ne_plugs_last_protocol; i++)
{
sub_protocol[i].header = 0;
sub_protocol[i].length = 0;
}
// |
// | Every packet is "ethernet"
sub_protocol[ne_plugs_ethernet].header = (void *)packet;
sub_protocol[ne_plugs_ethernet].length = packet_length;
// |
// | Check progressive layers of protocol numbers
p = nm_n2h16(packet->type);
if(p == ne_plugs_ethernet_arp)
{
sub_protocol[ne_plugs_arp].header = &packet->payload;
sub_protocol[ne_plugs_arp].length = 28;
}
else if(p == ne_plugs_ethernet_ip)
{
//
// | It's an IP packet! but what kind?
ip_packet = (ns_plugs_ip_packet *)packet->payload;
#if PLUGS_DEBUG
if (ip_packet->flags_fragment & nm_h2n16(0xbfff))
printf(" [plugs] | Ignored fragmented IP packet (0x%04x)\n",ip_packet->flags_fragment);
#endif // PLUGS_DEBUG
if
(
((ip_packet->version_header_length & 0x00f0) == 0x0040) // ipv4 only
&&
((ip_packet->flags_fragment & nm_h2n16(0xbfff)) == 0) // ignore fragments!
)
{
int ip_packet_length;
void *ip_payload;
// | stash the common ip packet info
// |
ip_packet_length = nr_n2h16(ip_packet->length);
sub_protocol[ne_plugs_ip].header = (void *)ip_packet;
sub_protocol[ne_plugs_ip].length = ip_packet_length;
// temporarily put header-length into this ip_payload_length variable
ip_payload_length = (ip_packet->version_header_length & 0x0f) * 4;
past_ip_packet = (void *)((int)ip_packet + ip_payload_length);
ip_payload = ((void *)ip_packet) + ip_payload_length;
// And now put the actual payload length into it
ip_payload_length = ip_packet_length - ip_payload_length;
if(ip_packet->protocol == ne_plugs_ip_icmp)
{
sub_protocol[ne_plugs_icmp].header = ip_payload;
sub_protocol[ne_plugs_icmp].length = ip_payload_length;
}
else if(ip_packet->protocol == ne_plugs_ip_udp)
{
sub_protocol[ne_plugs_udp].header = ip_payload;
sub_protocol[ne_plugs_udp].length = ip_payload_length;
}
else if(ip_packet->protocol == ne_plugs_ip_tcp)
{
sub_protocol[ne_plugs_tcp_raw].header = ip_payload;
sub_protocol[ne_plugs_tcp_raw].length = ip_payload_length;
sub_protocol[ne_plugs_tcp].header = ip_payload;
sub_protocol[ne_plugs_tcp].length = ip_payload_length;
}
}
}
// |
// | The array sub_protocol now shows what protocol(s)
// | this packet is.
// | Loop through the plugs and call any
// | plug that has a receiver-proc and wants this packet.
// |
plug_bitmask = 0; // start with no plug-recipients
got_tcp = 0; // at most one tcp plug gets each tcp packet
plug = g->plug;
for(i = 0; i < PLUGS_PLUG_COUNT; i++)
{
// |
// | Each plug is associated with a particular adapter.
// | We only look at plugs for the adapter that got
// | this packet, else skip along.
// |
if(a != &g->adapter[plug->flags & ne_plugs_flag_adapter_index_mask])
goto next_plug;
#if PLUGS_DEBUG
if(plug->flags & ne_plugs_flag_debug_rx)
{
int j;
// Dont print the "ignores" messages unless debug level 2
if( sub_protocol[plug->protocol].header || (PLUGS_DEBUG & 2) )
{
for(j = ne_plugs_last_protocol - 1; j > 0; j--)
if(sub_protocol[j].header)
break;
printf(" [plugs] +-----------------------\n");
printf(" [plugs] | plug %d (%s): %s a %d byte %s packet\n",i,
get_protocol_name(plug->protocol),
sub_protocol[plug->protocol].header ? "examines" : "ignores",
packet_length,
get_protocol_name(j));
}
}
#endif // PLUGS_DEBUG
if(sub_protocol[plug->protocol].header)
{
void *payload = 0;
int payload_length = 0;
int for_the_plug = 0; // well check this later
int ip_address_match;
int ethernet_address_match;
int port_match = 0;
ns_plugs_udp_packet *udpp = 0; // set to packet for udp OR tcp, for port numbers
// First things first:
// Do we even care the slightest bit about
// this ethernet packet?
// The hardware may or may not already
// be in promiscuous mode, but regardless,
// if the packet gets to this routine, we
// should verify that we want it.
ethernet_address_match =
((plug->flags & ne_plugs_flag_ethernet_all) != 0)
||
(
((plug->flags & ne_plugs_flag_ethernet_broadcast) != 0)
&&
(packet->destination_address.u32 == 0xffffFFFF)
&&
(packet->destination_address.l16 == 0xffff)
)
||
ethernet_address_equal
(&packet->destination_address,
&a->settings.ethernet_address
);
//
// Since ip, icmp, udp, and tcp all rely on
// matching source & destination ip addresses,
// we check those once out here, if the
// packet is ip flavored.
ip_address_match =
ethernet_address_match
&&
(packet->type == nm_h2n16(ne_plugs_ethernet_ip))
&&
(
((plug->flags & ne_plugs_flag_ip_all) != 0)
||
(
ip_address_equal
(
ip_packet->destination_ip_address,
a->settings.ip_address
)
&&
ip_address_equal
(
ip_packet->source_ip_address,
plug->remote_ip_address
)
)
);
//
// Since udp & tcp rely on port matching, we'll do that here, too
// udp & tcp have port info in same position (thankyou!)
if(sub_protocol[ne_plugs_udp].header || sub_protocol[ne_plugs_tcp_raw].header)
{
// |
// | Get port number from udp or tcp header -- first bytes
// | are port numbers in either case, so cast as udp header.
// |
udpp = sub_protocol[ne_plugs_udp].header;
if(!udpp)
udpp = sub_protocol[ne_plugs_tcp_raw].header;
port_match =
ip_address_match
&&
port_number_equal
(
udpp->destination_port,
plug->local_port
)
&&
port_number_equal
(
udpp->source_port,
plug->remote_port
);
}
#if PLUGS_DEBUG
if(plug->flags & ne_plugs_flag_debug_rx)
{
printf(" [plugs] | ");
if(sub_protocol[ne_plugs_udp].header || sub_protocol[ne_plugs_tcp_raw].header)
printf("port_match = %d, ",port_match);
if(sub_protocol[ne_plugs_ip].header)
printf("ip_address_match = %d, ",ip_address_match);
printf("ethernet_address_match = %d\n",ethernet_address_match);
//if(sub_protocol[ne_plugs_ip].header && !ip_address_match)
{
printf(" [plugs] | packet source ip address: ");
nr_plugs_print_ip_address(ip_packet->source_ip_address);
if(udpp)
printf("/%d",nr_n2h16(udpp->source_port));
printf("\n [plugs] | packet destination ip address: ");
nr_plugs_print_ip_address(ip_packet->destination_ip_address);
if(udpp)
printf("/%d",nr_n2h16(udpp->destination_port));
printf("\n [plugs] | local ip address: ");
nr_plugs_print_ip_address(a->settings.ip_address);
printf("/%d",nr_n2h16(plug->local_port));
printf("\n [plugs] | remote ip address: ");
nr_plugs_print_ip_address(plug->remote_ip_address);
printf("/%d",nr_n2h16(plug->remote_port));
printf("\n");
}
//if(!ethernet_address_match)
{
printf(" [plugs] | packet source ethernet address: ");
nr_plugs_print_ethernet_address(&packet->source_address);
printf("\n [plugs] | packet destination ethernet address: ");
nr_plugs_print_ethernet_address(&packet->destination_address);
printf("\n [plugs] | local ethernet address: ");
nr_plugs_print_ethernet_address(&a->settings.ethernet_address);
printf("\n");
}
}
#endif // PLUGS_DEBUG
//
// The packet is a type this plug listens to.
// Check out other filtration (ethernet address,
// port numbers, &c.)
switch(plug->protocol)
{
case ne_plugs_ethernet: // Ethernet proc gets whole packet
//
// Filter by ethernet address
if(ethernet_address_match)
{
payload = packet;
payload_length = packet_length;
for_the_plug = 1;
}
break;
case ne_plugs_arp: // ARP proc gets ARP header
//
// Filter by ethernet address
if(ethernet_address_match)
{
payload = packet->payload;
payload_length = sizeof(ns_plugs_arp_packet);
for_the_plug = 1;
}
break;
case ne_plugs_icmp: // ICMP proc gets ICMP header, IP gets own payload
case ne_plugs_ip:
if(ip_address_match)
{
payload = past_ip_packet;
payload_length = ip_payload_length;
for_the_plug = 1;
}
break;
case ne_plugs_udp:
if(port_match)
{
payload = ((ns_plugs_udp_packet *)past_ip_packet)->payload;
payload_length = ip_payload_length - sizeof(ns_plugs_udp_packet);
for_the_plug = 1;
}
break;
case ne_plugs_tcp_raw:
#if PLUGS_TCP
case ne_plugs_tcp:
#endif // PLUGS_TCP
if(port_match && !got_tcp)
{
payload = past_ip_packet;
payload = (void *)((int)payload
+ (nr_n2h16(((ns_plugs_tcp_packet *)past_ip_packet)->header_length_flags) >> 12) * 4);
payload_length = nr_n2h16(ip_packet->length)
- ((int)payload - (int)ip_packet);
#if PLUGS_TCP
if(plug->protocol == ne_plugs_tcp)
{
// | Handle specially elsewhere
int r;
r = r_tcp_dispatch(
a,
i + nk_plugs_plug_number_base,
plug,
sub_protocol,
payload,
payload_length);
// | only one tcp plug can get each
// | tcp packet to us.
if(!r)
got_tcp = 1;
}
else
#endif // PLUGS_TCP
{
for_the_plug = 1;
got_tcp = 1;
}
// The interaction between tcp and tcp_raw is
// tricky. A tcp_raw plug *could* steal your
// regular tcp packet, after all! best not to
// mix them, you know?
}
break;
} // switch
//
// If we've determined that this packet should
// be received by this plug, then either
// call the plug's callback proc right now,
// or mark the packet to be received on demand
// by the plug.
if(for_the_plug)
{
#if PLUGS_DEBUG
if(plug->flags & ne_plugs_flag_debug_rx)
{
printf(" [plugs] | packet accepted for plug\n");
nr_plugs_print_ethernet_packet(packet,packet_length," [plugs] | ");
}
#endif // PLUGS_DEBUG
// We know *some* plug wants it.
// If it happens to be in IP packet
// of any kind, let's just make sure
// that we have the originator in
// our ARP cache. Easy info!
if(sub_protocol[ne_plugs_ip].header)
r_add_to_arp_cache_from_ip_packet(a,packet);
if(plug->callback_proc)
{
#if PLUGS_DEBUG
if(plug->flags & ne_plugs_flag_debug_rx)
printf(" [plugs] | packet dispatched to callback proc %08x\n",
plug->callback_proc);
#endif // PLUGS_DEBUG
// Check to see if an exclusive plug is set. If it
// is, only call callback routines for the exclusive plug
// and the APR plug.
// We must also always respond to ARP packets, otherwise,
// The sender might lose track of who we are.
if ((g->exclusive_plug_handle == 0) ||
(plug==r_plug_from_handle(g->exclusive_plug_handle)) ||
(plug == r_plug_from_handle (a->arp_plug)))
{
(plug->callback_proc)
(i + nk_plugs_plug_number_base,
plug->callback_context,
sub_protocol,
payload,
payload_length);
}
}
else
plug_bitmask |= 1 << i;
}
#if PLUGS_DEBUG
else
{
if((plug->flags & ne_plugs_flag_debug_rx)
&& !got_tcp)
{
printf(" [plugs] | packet not sent to plug\n");
}
}
#endif // PLUGS_DEBUG
} // packet protocol include plug protocol
next_plug:
plug++;
} // for (i), each plug
#if PLUGS_TCP
// |
// | If it was a tcp packet to this IP address, and
// | nobody handled it, reply with an incisive RST
// |
if(sub_protocol[ne_plugs_tcp].header
&& ethernet_address_equal
(
&packet->destination_address,
&a->settings.ethernet_address
)
&& ip_address_equal(ip_packet->destination_ip_address,a->settings.ip_address)
&& !got_tcp)
{
int tcp_flags;
ns_plugs_tcp_packet *tcp_packet;
// |
// | Make sure we have the ethernet address
// | for this packet before we try to send
// |
tcp_packet = sub_protocol[ne_plugs_tcp].header;
tcp_flags = nr_n2h16(tcp_packet->header_length_flags);
if(!(tcp_flags & ne_plugs_flag_tcp_rst))
{
// |
// | But, don't respond to rst with rst!
// |
r_add_to_arp_cache_from_ip_packet(a,packet);
// nr_plugs_print_ethernet_packet
// (
// packet,
// packet_length,
// "NO: "
// );
r_tcp_send(0,0,
ne_plugs_flag_tcp_rst | ne_plugs_flag_tcp_ack,
sub_protocol,0);
}
}
#endif // PLUGS_TCP
// If any plug_bitmask, and queing enabled, queue the packet
go_home:
g->in_dispatch--;
return 0;
}
int nr_plugs_create(int *plugHandleOut,
int protocol,
host_16 port,
nr_plugs_receive_callback_proc callback,
void *callback_context,
long flags)
{
s_plugs_globals *g = &ng_plugs_globals;
int i;
s_plugs_plug *plug;
if((!g->running)
|| ( ((unsigned long)(flags & ne_plugs_flag_adapter_index_mask)) >= ((unsigned long)g->adapter_count)) )
return ne_plugs_error_not_initialized; // uninitialized error
port = nr_h2n16(port);
// port number zero means "give me a fresh port number"
if(port == 0)
port = get_port_number(g);
plug = g->plug;
for(i = 0; i < PLUGS_PLUG_COUNT; i++)
{
if(plug->protocol == 0)
{
//
// We found an unused plug, lets use it.
// (??? how to mark it until its connected?)
//
// zero out all fields?
plug->protocol = protocol;
plug->local_port = port;
plug->callback_proc = callback;
plug->callback_context = callback_context;
plug->flags = flags;
#if PLUGS_TCP
plug->tcp_state = e_plugs_tcp_inert;
plug->tcp_manual_window_size = 1024;
plug->tcp_sn_local = wellspring_of_sequence_numbers();
// This statement amounts to: "the current
// sequence number hasn't been acknowledged yet."
plug->tcp_last_acknowledged_sn_local =
plug->tcp_sn_local - 0x100;
#endif // PLUGS_TCP
*plugHandleOut = i + nk_plugs_plug_number_base;
return 0;
}
plug++;
}
return ne_plugs_error_too_many_plugs;
}
int nr_plugs_destroy(int plug_handle)
{
int result = 0;
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_plug *plug = r_plug_from_handle(plug_handle);
// | First, we'll close the connection
// | (Only does anything if it happens to
// | be a tcp plug, and then only if tcp is
// | even in the build.)
#if PLUGS_TCP
result = nr_plugs_connect(plug_handle,0,0,0);
#endif
// | "Destroying" a plug is easy: just
// | mark it as unused, and everyone
// | will ignore it.
plug->protocol = 0;
return result;
}
// +-----------------------
// | nr_plugs_tcp_close
// |
// | Graceful closure of TCP connection
// |
// | We send a FIN. We either do or don't wait for the other side to
// | ACK, depending on how polite the caller wants to be.
// |
nr_plugs_tcp_close (int plug_handle, unsigned int ack_wait_timeout)
{
#if PLUGS_TCP
s_plugs_plug* plug;
int result;
unsigned long t_now;
unsigned long t_start;
plug = r_plug_from_handle(plug_handle);
// If this isn't a TCP plug, or it wasn't connected in the first
// place, then it was a short (and successful) trip.
//
if((plug->protocol != ne_plugs_tcp) ||
(plug->tcp_state <= e_plugs_tcp_listening))
{
return 0; // Consider it closed.
}
plug->tcp_state = e_plugs_tcp_disconnecting;
// Send a FIN packet.
//
result = r_tcp_send (
plug_handle,
plug,
ne_plugs_flag_tcp_fin | ne_plugs_flag_tcp_ack,
0,
0
);
if (result < 0)
return result;
// We expect them to ACK the FIN we just sent them.
//
if (ack_wait_timeout > 0) {
result = r_tcp_wait_for_ack (plug, ack_wait_timeout);
if (result)
return result;
}
// Now we expect them to send a FIN of their own.
// When they send the FIN, the TCP incoming-packet-handling
// routine (r_tcp_dispatch) will see it and flip our state to
// "inert." All we have to do is watch this variable.
//
t_start = nr_timer_milliseconds();
t_now = t_start;
while (t_now - t_start < ack_wait_timeout){
nr_plugs_idle();
if (plug->tcp_state != e_plugs_tcp_disconnecting) {
// Plug went "inert" because a FIN came in.
return 0;
}
t_now = nr_timer_milliseconds();
}
// If we got to here: we never got our FIN from the other guy.
return -1;
#else // PLUGS_TCP
// this shouldn't be called when PLUGS_TCP is zero,
// but we will return zero if it is
return 0;
#endif // PLUGS_TCP
}
//
// nr_plugs_connect
//
// Some kinds of port _must_ be connected
// before they can send or receive packets.
int nr_plugs_connect(int plug_handle,
char *remote_name,
host_32 remote_ip_address,
host_16 remote_port)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_plug *plug;
int result = 0;
plug = r_plug_from_handle(plug_handle);
if(remote_name)
{
result = nr_plugs_name_to_ip(remote_name,&remote_ip_address);
if(result)
goto go_home;
remote_ip_address = nr_n2h32(remote_ip_address);
}
#if PLUGS_TCP
// |
// | Any existing TCP connection?
// | Close it. We do a partial
// | close: we send a FIN and pretend
// | we're completely done. When they
// | send their ACK and FIN, we'll just
// | slam them with a RST. This is
// | impolite, but happens to be what
// | Windows NT does, too, so I don't
// | feel too bad, actually. -- dvb
// |
if((plug->protocol == ne_plugs_tcp)
&& (plug->tcp_state > e_plugs_tcp_listening))
{
// Use zero-timeout: don't wait for acknowledge.
printf ("Connect: already open. Closing\n");
result = nr_plugs_tcp_close (plug_handle, 0);
if(result)
goto go_home;
}
#endif // PLUGS_TCP
plug->remote_ip_address = nr_h2n32(remote_ip_address);
plug->remote_port = nm_h2n16(remote_port);
#if PLUGS_TCP
// |
// | Open a new connection?
// | They may have said connect to 0.0.0.0/0,
// | which just means "hang up and forget it."
// | If not, well, initiate connection.
// |
if((plug->protocol == ne_plugs_tcp) && plug->remote_ip_address)
{
int retry_count;
long t0,t;
plug->tcp_sn_local = wellspring_of_sequence_numbers();
for(retry_count = 0; retry_count < nk_plugs_tcp_retry_count; retry_count++)
{
t0 = nr_timer_milliseconds();
result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_syn,0,0);
if(result)
goto go_home;
plug->tcp_state = e_plugs_tcp_connecting;
printf ("\n[%08X]: Connecting... |", plug);
while(nr_timer_milliseconds() - t0 < nk_plugs_tcp_retry_interval)
{
nr_plugs_idle();
if(plug->tcp_state == e_plugs_tcp_connected)
goto done_trying_to_connect;
if(plug->tcp_state == e_plugs_tcp_inert)
{
result = ne_plugs_error_tcp_connection_refused;
goto done_trying_to_connect;
}
}
}
result = ne_plugs_error_tcp_timed_out;
printf ("\n[%08X]: Connection timed out. |");
done_trying_to_connect:
;
}
#endif // PLUGS_TCP
go_home:
return result;
}
// +--------
// | Bracketing the tcp-specific routines...
#if PLUGS_TCP
// +------------------------
// | nr_plugs_get_tcp_local_sequence_number
// |
// | For your plug (specified by-handle), gives
// | the current local sequence number. Zero if not connected.
// |
unsigned long nr_plugs_get_tcp_local_sequence_number (int plug_handle)
{
s_plugs_plug* plug = r_plug_from_handle (plug_handle);
// Simple relevance check:
if ((!plug) ||
(plug->protocol != ne_plugs_tcp))
return ne_plugs_error_not_initialized;
if (plug->tcp_state < e_plugs_tcp_connected)
return 0;
return plug->tcp_sn_local;
}
// +------------------------
// | nr_plugs_get_tcp_last_acknowledged_sequence_number
// |
// | For your plug (specified by-handle), gives
// | the last local sequence-number officially acknowledged
// | by the remote side. This is the sequence-number of the
// | last packet known to have been transmitted correctly.
// |
unsigned long nr_plugs_get_tcp_last_acknowledged_sequence_number
(int plug_handle)
{
s_plugs_plug* plug = r_plug_from_handle (plug_handle);
// Simple relevance check:
if ((!plug) ||
(plug->protocol != ne_plugs_tcp))
return ne_plugs_error_not_initialized;
if (plug->tcp_state < e_plugs_tcp_connected)
return 0;
return plug->tcp_last_acknowledged_sn_local;
}
// +------------------------
// | nr_plugs_get_tcp_remote_window_size
// |
// | For your plug (specified by-handle), gives
// | the last window-size officially reported
// | by the remote side.
// |
// | Negative result means: Plug no longer connected.
// |
unsigned int nr_plugs_get_tcp_remote_window_size (int plug_handle)
{
s_plugs_plug* plug = r_plug_from_handle (plug_handle);
// Simple relevance check:
if ((!plug) ||
(plug->protocol != ne_plugs_tcp))
return ne_plugs_error_not_initialized;
// If the plug has gotten disconnected for some reason,
if (plug->tcp_state < e_plugs_tcp_connected)
return -1;
return plug->tcp_remote_window_size;
}
// +------------------------
// | nr_plugs_tcp_rewind_local_sequence_number
// |
// | Call this to reset your plug's local sequence number
// | to a past value (which you provide, on the honor system).
// |
// | You only need to do this if you're trying to re-send
// | a packet that you have reason to believe failed in the
// | past.
// |
int nr_plugs_tcp_rewind_local_sequence_number(int plug_handle,
unsigned long old_sn_local)
{
s_plugs_plug* plug = r_plug_from_handle (plug_handle);
// Simple relevance check:
if ((!plug) ||
(plug->protocol != ne_plugs_tcp))
return ne_plugs_error_not_initialized;
plug->tcp_sn_local = old_sn_local;
return 0;
}
// +------------------------
// | nr_plugs_wait_for_tcp_acknowledge
// |
// | Just a wrapper around r_tcp_wait_for_ack(),
// | but is more user-friendly because it takes a
// | plug-handle instead of a plug.
// |
int nr_plugs_wait_for_tcp_acknowledge
(int plug_handle,
int timeout_milliseconds)
{
s_plugs_plug* plug = r_plug_from_handle (plug_handle);
return r_tcp_wait_for_ack (plug, timeout_milliseconds);
}
// +------------------------
// | r_tcp_wait_for_ack
// |
// | Sit around waiting for our most-recent TCP packet to be
// | acknowledged.
// |
// | Returns zero if the packet is acknowledged within the timeout
// | period--nonzero otherwise.
// |
// | Default timeout period is 100ms
// |
int r_tcp_wait_for_ack
(s_plugs_plug* plug,
unsigned int timeout_milliseconds)
{
unsigned int t_start = nr_timer_milliseconds();
unsigned int t_now;
unsigned int gap;
if (timeout_milliseconds <= 0)
return 0;
t_now = t_start;
while ((t_now - t_start) < timeout_milliseconds) {
nr_plugs_idle();
if (plug->tcp_last_acknowledged_sn_local == plug->tcp_sn_local)
return 0; // Success!
// While we're waiting around, someone might send us a RST,
// which would result in our immediate disconnection.
// That would happen in the r_tcp_dispatch routine that
// copes with incoming packets. (during nr_plugs_idle, or IRQs)
//
if (plug->tcp_state < e_plugs_tcp_connected)
return -1;
t_now = nr_timer_milliseconds();
}
gap = ((unsigned int) plug->tcp_sn_local) -
((unsigned int) plug->tcp_last_acknowledged_sn_local);
printf (" No ack. local=%d. Last-ack=%d. Diff=%d\n",
plug->tcp_sn_local, plug->tcp_last_acknowledged_sn_local,
gap);
return -1; // Timeout. No ack. Return "NO" code.
}
// +---------------------------------------
// | Listen -- let a tcp port wait for a connection
int nr_plugs_listen(
int plug_handle,
nr_plugs_listen_callback_proc listen_proc,
void *listen_proc_context
)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_plug *plug;
plug = r_plug_from_handle(plug_handle);
if(plug->tcp_state != e_plugs_tcp_inert)
nr_plugs_connect(plug_handle,0,0,0);
//return ne_plugs_error;
plug->tcp_listen_proc = listen_proc;
plug->tcp_listen_proc_context = listen_proc_context;
plug->tcp_state = e_plugs_tcp_listening;
plug->remote_ip_address = -1;
plug->remote_port = -1; // accept from anyone...
return 0;
}
int nr_plugs_tcp_ready_for_next (int plug_handle, int window_size)
{
s_plugs_plug *plug;
plug = r_plug_from_handle(plug_handle);
if (!plug)
return ne_plugs_error_not_initialized;
if (!plug->flags & ne_plugs_flag_tcp_manual_flow)
return ne_plugs_error_not_initialized;
plug->tcp_manual_window_size = window_size;
return r_tcp_send(plug_handle,
plug,
ne_plugs_flag_tcp_ack,
0,
0
);
}
// +--------------------------------------------
// | Utility routine for composing and sending
// | tcp packets. Mostly it can figure out
// | what to do just from the flags.
// |
// | Exceptions:
// | if plug is 0, then payload is actually the
// | sub_protocol array.
// |
int r_tcp_send(int plug_handle,s_plugs_plug *plug,int tcp_flags,void *payload,long payload_length)
{
int result;
unsigned char x[1500];
ns_plugs_tcp_packet *tcp_reply; // reply
net_32 destination_ip_address;
int send_to_flags = plug->flags & ne_plugs_flag_adapter_index_mask;
//r_set_irqs_for_plug (plug, 0);
tcp_reply = (ns_plugs_tcp_packet *)x;
nr_zerorange((char *)tcp_reply,sizeof(ns_plugs_tcp_packet));
if(!plug)
{
// |
// | If plug is 0, the *only* thing we can
// | do is send an RST to the sender, presumed
// | to be the source of an ip_packet overloading
// | the input parameter "payload"
// |
ns_plugs_packet *sub_protocol = payload;
ns_plugs_ip_packet *ipp = sub_protocol[ne_plugs_ip].header; // what we reply to
ns_plugs_tcp_packet *tcpp = sub_protocol[ne_plugs_tcp].header;
destination_ip_address = ipp->source_ip_address;
tcp_reply->source_port = tcpp->destination_port;
tcp_reply->destination_port = tcpp->source_port;
tcp_reply->sequence_number = tcpp->acknowledgement_number;
tcp_reply->acknowledgement_number = nr_h2n32(nr_n2h32(tcpp->sequence_number) + 1);
tcp_reply->header_length_flags =
nr_h2n16(0x5000 | ne_plugs_flag_tcp_rst | ne_plugs_flag_tcp_ack);
payload = 0;
payload_length = 0;
send_to_flags = nk_plugs_default_debug;
}
else
{
destination_ip_address = plug->remote_ip_address;
tcp_reply->source_port = plug->local_port;
tcp_reply->destination_port = plug->remote_port;
if ((plug->flags & ne_plugs_flag_tcp_manual_flow) &&
(plug->tcp_state >= e_plugs_tcp_connected) )
tcp_reply->window_size = nm_h2n16(plug->tcp_manual_window_size);
else
tcp_reply->window_size = nm_h2n16(1024);
// | Always send ack & seq numbers...
tcp_reply->acknowledgement_number = nr_h2n32(plug->tcp_sn_remote);
tcp_reply->sequence_number = nr_h2n32(plug->tcp_sn_local);
tcp_reply->header_length_flags =
nr_h2n16(0x5000 | (tcp_flags & ne_plugs_flag_tcp_mask));
// | If SYN or FIN, bump our local seq by 1,
// | else, if PSH, put payload bump local seq by payload size
if(tcp_flags & (ne_plugs_flag_tcp_syn | ne_plugs_flag_tcp_fin))
plug->tcp_sn_local++;
else if(tcp_flags & ne_plugs_flag_tcp_psh)
{
// |
// | Copy (byte by byte) the payload)
// |
int i;
plug->tcp_sn_local += payload_length;
for(i = 0; i < payload_length; i++)
tcp_reply->payload[i] = ((char *)payload)[i];
}
if(tcp_flags & ne_plugs_flag_tcp_rst) // reset? window is zero, thank you.
tcp_reply->window_size = 0;
}
// |
// | Use the overload-feature of nr_plugs_send, and treat
// | our carefully constructed packet as an ip packet,
// | since we're giving it the payload already all
// | put together.
// |
// | (payload_length now includes tcp header + tcp payload)
payload_length += sizeof(ns_plugs_tcp_packet);
result = nr_plugs_send_to(ne_plugs_ip,tcp_reply,payload_length,
ne_plugs_flag_tcp_checksum | send_to_flags,
destination_ip_address,tcp_reply->destination_port);
// r_set_irqs_for_plug (plug, 1);
return result;
}
// +-----------------------------------------
// | Routine for handling non-raw tcp connections
// | Return 0 if the packet is handled by this
// | port, else return -1. Whoever calls this
// | routine may decide to send a tcp RST to
// | the sender if no plug handles the tcp connection.
int r_tcp_dispatch(
s_plugs_adapter_entry *a,
int plug_handle,
s_plugs_plug *plug,
ns_plugs_packet *sub_protocol,
void *payload,
int payload_length)
{
int result = 0;
host_32 seq;
host_32 ack;
host_16 tcp_flags;
net_16 remote_window;
ns_plugs_ip_packet *ipp = sub_protocol[ne_plugs_ip].header;
ns_plugs_tcp_packet *tcpp = sub_protocol[ne_plugs_tcp].header;
// |
// | Ensure that we can respond to this
// | packet, mac-address-wise...
// |
r_add_to_arp_cache_from_ip_packet(a,sub_protocol[ne_plugs_ethernet].header);
// |
// | Extract some basic info from the incoming packet
// |
seq = nr_n2h32(tcpp->sequence_number);
ack = nr_n2h32(tcpp->acknowledgement_number);
tcp_flags = nr_n2h16(tcpp->header_length_flags);
remote_window = nr_n2h16(tcpp->window_size);
// |
// | Handle the various tcp packet kinds in
// | an exploded if{do and go home} list
// |
if(tcp_flags & ne_plugs_flag_tcp_syn) // | connection request or confirm
{
int x = nr_timer_milliseconds();
if(plug->tcp_state == e_plugs_tcp_connecting)
{
// |
// | We initiated a connection, and
// | our remote target has, apparently,
// | responded.
// | Swell, we are connected.
// |
plug->tcp_sn_remote = seq + 1;
result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0);
if(!result){
plug->tcp_state = e_plugs_tcp_connected;
printf ("\n[%08X]: Connection granted. |", plug);
}
} // if connecting & got syn
else if(plug->tcp_state == e_plugs_tcp_listening)
{
// |
// | Listening -- call application acceptance routine
// | (Really, it's an application rejection routine,
// | since if there isn't one, we automatically accept.
// |
if(plug->tcp_listen_proc)
{
result = (plug->tcp_listen_proc)
(plug_handle,
plug->tcp_listen_proc_context,
nr_n2h32(ipp->source_ip_address),
nr_n2h16(tcpp->source_port));
if(result)
goto go_home;
}
plug->tcp_connection_time = x;
plug->remote_ip_address = ipp->source_ip_address;
plug->remote_port = tcpp->source_port;
plug->tcp_sn_remote = seq + 1;
plug->tcp_remote_window_size = remote_window;
plug->tcp_sn_local =
wellspring_of_sequence_numbers();
plug->tcp_last_acknowledged_sn_local =
plug->tcp_sn_local - 0x100;
// |
// | Send ACK & SYN in return, to accept the connection
// |
result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack | ne_plugs_flag_tcp_syn,0,0);
if(!result)
{
plug->tcp_state = e_plugs_tcp_connected;
}
} // if listening & got syn
else
{
// |
// | Not listening or connecting? Then
// | a SYN has no business with us.
// | Return an error code, we cannot accept.
// |
printf ("TCP connection refused.\n");
return_error:
result = ne_plugs_error;
}
goto go_home;
} // if syn
// +---------------------------------
// | If we're not connected, no other packet is interesting
if(plug->tcp_state <= e_plugs_tcp_listening)
{
goto return_error;
}
// +-------------------------------------------
// | If the packet has an ACK in it, we'll check
// | it against our own numbers, and maybe resent
// | an ack to get back into phase...
// |
// | We do this before checking the payload,
// | because the remote count in the packet
// | does not include the current payload.
// |
// | We also stash-away the local sequence number
// | that they're acknowledging. This lets us
// | check (elsewhere) if they've been paying attention.
if(tcp_flags & ne_plugs_flag_tcp_ack)
{
// |
// | Just sending us an ack?
// | eh, ok, great. But if we're out of sync, resend
// | our own ack. (This also ignores a FIN if we're
// | out of phase.)
// |
if(seq != plug->tcp_sn_remote)
{
//printf ("Out-of-sequence (remote) packet detected. SEQ!\n");
r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0);
goto go_home;
}
plug->tcp_last_acknowledged_sn_local = ack;
plug->tcp_remote_window_size = remote_window;
}
// +---------------------------------
// | Second things second: if there's any
// | data with the packet -- and there could
// | be, even with a FIN packet! -- we must
// | pass it straight to the plug's
// | callback.
if(payload_length)
{
int do_ack_packet = 1;
// |
// | They're sending us some bytes!
// | (By the way, the "PSH" flag is
// | irrelevant, it's used to indicate
// | that the bytes are special or something.)
// |
if(seq == plug->tcp_sn_remote)
{
int callback_result;
// |
// | We got some legit bytes, let's acknoweldge
// | them and pass the data on to the application.
// |
plug->tcp_sn_remote += payload_length;
plug->tcp_remote_window_size = remote_window;
// This is only meaningful (or useful) for
// manual flow-control, if enabled.
// If manual-flow is disabled, this variable
// is ignored.
//
callback_result = (plug->callback_proc)
(plug_handle,
plug->callback_context,
sub_protocol,
payload,
payload_length);
if (plug->flags & ne_plugs_flag_tcp_manual_flow) {
if (callback_result >= 0)
// In manual-flow mode, positive result is
// new window-size
//
plug->tcp_manual_window_size = callback_result;
else
// Negative result means: Don't ack this
// one.
do_ack_packet = 0;
}
}
else
{
// |
// | Something bad has happened: they have a
// | sequence number that is not what we expect.
// |
// | The best we can do is send an ACK for the
// | sequence number we think we do want.
// |
// | (actually, this "else" clause is just a
// | comment, to fall to the ack below.)
}
if (do_ack_packet)
r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0);
} // payload present
// +---------------------------------
// | FIN
// | If they wish to close connection, then do it.
if (tcp_flags & ne_plugs_flag_tcp_fin) {
if (plug->tcp_state == e_plugs_tcp_disconnecting) {
// We expected this.. We sent a FIN earlier,
// and this is their FIN in reply.
// We don't need to do anything special, except
// close-down the connection, below.
}
// They were good enough to send us a packet...
// We have to ack it, even though it's the end.
// This protocol sure is polite.
//
plug->tcp_sn_remote++; // | Bump the ack
plug->tcp_remote_window_size = remote_window;
if(plug->tcp_state == e_plugs_tcp_connected) {
// | OK, they're requesting that the connection
// | be closed by sending us an unsolicited FIN.
// | Send a FIN of our own, and wait (a little
// | while) for their ACK:
// |
r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_fin|ne_plugs_flag_tcp_ack,0,0);
}else{
r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0);
}
r_tcp_plug_set_inert(plug);
// |
// | Notify the application that the
// | tcp connection has been closed
// | by sending a payload size of 0.
//
(plug->callback_proc)
(plug_handle,
plug->callback_context,
sub_protocol,
0,
0);
goto go_home;
}
if(tcp_flags & ne_plugs_flag_tcp_rst) {
// |
// | They want an immediate disconnect. Fine.
// |
r_tcp_plug_set_inert(plug);
// |
// | Notify the application that the
// | tcp connection has been closed
// | by sending a payload size of 0.
//
(plug->callback_proc)
(plug_handle,
plug->callback_context,
sub_protocol,
0,
0);
goto go_home;
}
go_home:
return result;
}
void r_tcp_plug_set_inert (s_plugs_plug* plug)
{
#if PLUGS_TCP
plug->tcp_state = e_plugs_tcp_inert;
plug->tcp_manual_window_size = 0;
#endif // PLUGS_TCP
plug->remote_ip_address = 0;
plug->remote_port = 0;
}
#endif // PLUGS_TCP
// --------------------------
// DNS lookup
#if PLUGS_DNS
// support routines for parsing those
// troublesome unaligned dns queries
// Given a pointer to some bytes to emit to,
// and an int, emit the two bytes bigendian.
// This is done byte-by-byte, so is ok
// to be unaligned.
static void p16(unsigned char **wp,int v)
{
unsigned char *w;
w = *wp;
*w++ = (v & 0x0000ff00) >> 8;
*w++ = (v & 0x000000ff);
*wp = w;
}
// Given a pointer to some bytes to emit to,
// and a host name, put in the host name
// in the weird form required by dns.
// Advance the pointerpointer.
static void pn(unsigned char **wp,char *name)
{
unsigned char *w;
unsigned char *label_begin;
unsigned char label_len;
int c;
// | Hey man, sorry about the gotos. -- dvb 2001.03.23
w = *wp;
do_label:
label_begin = w++;
label_len = 0;
do_label_char:
c = *name++;
if(c == 0 || c == '.')
{
*label_begin = label_len;
if(c)
goto do_label;
else
{
*w++ = 0;
*wp = w;
return;
}
}
label_len++;
*w++ = c;
goto do_label_char;
}
int g16(unsigned char **wp)
{
unsigned char *w = *wp;
int v;
v = *w++;
v = (v << 8) + *w++;
*wp = w;
return v;
}
// Skip over strange length-delimited dns "label"
void skip_label(unsigned char **wp)
{
unsigned char *w = *wp;
int label_length;
while((label_length = *w++) != 0)
if(label_length > 64)
{
w++;
goto go_home;
}
else
w += label_length;
go_home:
*wp = w;
}
static int r_plugs_dns_proc
(
int plug_handle,
void *context,
ns_plugs_packet *p,
void *payload,
int payload_length
)
{
unsigned char *w = payload;
unsigned char **wp = &w;
int v;
int questions;
int answers;
int result = 0;
s_plugs_globals *g = &ng_plugs_globals;
// identifier... is it the one we sent out?
v = g16(wp);
if(v != g->dns_identifier) // not for us, just ignore
goto go_home;
// flags... did we get an answer?
v = g16(wp);
if(
(!(v & 0x8000)) // it's not an answer?
||
((v & 0x000f) == 3) // it's an error?
)
{
failure:
g->dns_query_state = 3;
goto go_home;
}
// questions & answers
questions = g16(wp);
answers = g16(wp);
if(!answers)
goto failure;
w += 4; // skip over authorities & additional
// skip over questions
while(questions--)
{
skip_label(wp);
// skip over query type & class
w += 4;
}
// take first answer of type 1 as the answer!
while(answers--)
{
skip_label(wp);
v = g16(wp); // query type
if(v == 1) // 'A' (ip address)
{
long vl;
w += 8; // skip type, class, & ttl, and assume lenght = 4...
vl = g16(wp);
vl = (vl << 16) + g16(wp);
g->dns_query_state = 2; // answered ok
g->dns_query_result = nr_h2n32(vl);
goto go_home;
}
else
{
// some query type we dont care about...
w += 6;
v = g16(wp); // resource length
w += v;
}
}
// got to here? No answers with our IP address?? aww.
result = ne_plugs_error_unwanted_reply;
go_home:
return result;
}
#endif // PLUGS_DNS
int nr_plugs_name_to_ip(char *host_name,net_32 *host_ip_address_out)
{
#if PLUGS_DNS
unsigned char p[256]; // arbitrary upper bound on query size
int len; // length of constructed query
unsigned char *w;
unsigned char **wp;
s_plugs_globals *g = &ng_plugs_globals;
int id;
long t1,ti; // current time, last req time
int retry_count;
net_32 host_ip_address;
int result = 0;
w = p;
wp = &w;
g->dns_identifier = g->ip_identifier++;
// Build the dns message, and then possibly reuse it several times
// dns header
p16(wp,g->dns_identifier); // dns id, to recognize answer
p16(wp,0x0100); // dns flags: recursion desired
p16(wp,1); // 1 question
p16(wp,0); // 0 answers
p16(wp,0); // 0 authorities
p16(wp,0); // 0 additional
// dns question
pn(wp,host_name); // host name to look up
p16(wp,1); // query type: 'A' IP address
p16(wp,1); // query class: 1 = internet, blah blah
w = *wp;
len = w - p;
ti = nr_timer_milliseconds() - nk_plugs_dns_retry_interval; // so first timeout always true
g->dns_query_state = 0; // so we can wait for it to change...
g->dns_query_result = 0;
retry_count = nk_plugs_dns_retry_count;
dns_query_loop:
// Time to send another query?
t1 = nr_timer_milliseconds();
if(t1 - ti > nk_plugs_dns_retry_interval)
{
retry_count--;
if(retry_count < 0)
{
result = ne_plugs_error_dns_timed_out;
goto go_home;
}
nr_plugs_send(g->dns_plug,p,len,0);
ti = t1;
}
// Idle the system, and wait for godot.
nr_plugs_idle();
if(g->dns_query_state) // godot here already?
{
if(g->dns_query_state == 3) // failure?
result = ne_plugs_error_dns_not_found;
goto go_home; // we are done, in any case.
}
goto dns_query_loop;
go_home:
*host_ip_address_out = g->dns_query_result;
return result;
#else // PLUGS_DNS
*host_ip_address_out = 0;
return ne_plugs_error_feature_disabled;
#endif // else PLUGS_DNS
}
// ----------------------------
// r_add_to_arp_cache
//
// insert an entry to the cache (at extant spot, if possible)
static void r_add_to_arp_cache(s_plugs_adapter_entry *a,net_32 ip_address,net_32 ethu32,net_16 ethl16)
{
s_plugs_arp_cache_entry *w;
int i;
w = a->arp_cache;
for(i = 0; i < nk_plugs_arp_cache_count; i++)
{
if(w->ip_address == ip_address)
goto found_a_spot;
w++;
}
// no existing ip entry? choose the next one
w = &a->arp_cache[a->arp_cache_walker];
a->arp_cache_walker++;
if(a->arp_cache_walker == nk_plugs_arp_cache_count)
a->arp_cache_walker = 0;
found_a_spot:
w->ip_address = ip_address;
w->ethernet_address.u32 = ethu32;
w->ethernet_address.l16 = ethl16;
}
// --------------------------
// r_add_to_arp_cache_from_ip_packet
//
// Given an ethernet packet known to contain an IP header,
// add the source's address to the cache.
// This is great for imminent replies to packets.
static void r_add_to_arp_cache_from_ip_packet(s_plugs_adapter_entry *a,ns_plugs_ethernet_packet *p)
{
net_32 ip_address;
net_48 ethernet_address;
// pull the info from the packet
ip_address = ((ns_plugs_ip_packet *)(&p->payload))->source_ip_address;
ethernet_address = p->source_address;
// Is it outside our LAN? If so, any ethernet
// address actually refers to the router.
if((ip_address ^ a->settings.ip_address) & a->settings.subnet_mask)
ip_address = a->settings.gateway_ip_address;
// And put in cache!
r_add_to_arp_cache(a,ip_address,ethernet_address.u32,ethernet_address.l16);
}
// --------------------------
// nr_plugs_ip_to_ethernet:
// invokes arp if necessary, but ONLY if we're not in_dispatch. Reentrant, doncha know.
// The "flags" parameter is taken from the plug that's calling
// us, and the routine prints errors if ne_plugs_flag_debug_tx is set.
//
int nr_plugs_ip_to_ethernet(int adapter_index,net_32 ip_address,net_48 *ethernet_address_out,long flags)
{
s_plugs_globals *g = &ng_plugs_globals;
int result = 0;
int i;
s_plugs_arp_cache_entry *w;
int requests_sent;
long t0,t1; // time we started, current time
long ti; // time of last arp-request
long retry_count; // how many times we've polled
s_plugs_adapter_entry *a = &g->adapter[adapter_index];
// |
// | Two things indicate an ethernet broadcast:
// | The ethernet broadcast flag, or ip address 0xffffffff
// |
// | The ethernet address for an ethernet broadcast is easy! 0xffffFFFFffff
// |
if((flags & ne_plugs_flag_ethernet_broadcast) || (ip_address == 0xffffFFFF))
{
ethernet_address_out->u32 = 0xffffFFFF;
ethernet_address_out->l16 = 0xFFFF;
goto go_home;
}
// Is it outside our LAN? If so, we must
// send to the router, not direct
if((ip_address ^ a->settings.ip_address) & a->settings.subnet_mask)
ip_address = a->settings.gateway_ip_address;
t0 = nr_timer_milliseconds();
ti = t0 - nk_plugs_arp_retry_interval; // so first timeout test always does it
retry_count = 0;
scan_cache:
// Already in the cache?
//
// Receive it right now if possible!
// nr_plugs_idle();
w = a->arp_cache;
for(i = 0; i < nk_plugs_arp_cache_count; i++)
{
if(w->ip_address == ip_address)
{
// entry there, but no arp-response yet?
if(!(w->ethernet_address.u32 || w->ethernet_address.l16))
goto present_but_unresolved;
*ethernet_address_out = w->ethernet_address;
goto go_home;
}
w++;
}
// Add a spot in the ARP cache, with zeroed ethernet
r_add_to_arp_cache(a,ip_address,0,0);
present_but_unresolved:
// We put entries in the cache with ethernet_address == 0
// to signify that we want it. The ARP handler only fills
// in mac addr's that already have an entry for the ip.
// Not found in cache
// If we're already in dispatch, we cannot conduct a
// transaction, so just fail
if(g->in_dispatch)
{
result = ne_plugs_error_arp;
goto go_home;
}
//
// Should we send an ARP request?
t1 = nr_timer_milliseconds();
if(t1 - ti < nk_plugs_arp_retry_interval)
goto scan_cache;
ti = t1; // retry timer shall go again from here...
if(retry_count >= nk_plugs_arp_retry_count)
{
result = ne_plugs_error_arp_timed_out;
goto go_home;
}
retry_count++;
// Not in the cache, & is time to send out a request, and wait...
// If we wait too long, well, report an error then.
{
ns_plugs_arp_packet request;
request.hardware_type = nm_h2n16(1);
request.protocol_type = nm_h2n16(0x0800);
request.hardware_size = 6; // 6 for ethernet
request.protocol_size = 4; // 4 for IP
request.op = nm_h2n16(1); // request
request.sender_ethernet_address = a->settings.ethernet_address;
request.sender_ip_address = a->settings.ip_address;
request.target_ethernet_address.u32 = 0;
request.target_ethernet_address.l16 = 0;
request.target_ip_address = ip_address;
#if PLUGS_DEBUG
if(flags & ne_plugs_flag_debug_tx)
{
printf(" [plugs] | sending arp request for ");
nr_plugs_print_ip_address(request.target_ip_address);
printf("\n");
}
#endif // PLUGS_DEBUG
result = nr_plugs_send(a->arp_plug,&request,28,0);
if(result)
goto go_home;
}
goto scan_cache;
go_home:
return result;
}
void packet_append_bytes(char **dst,void *src, int length)
{
char *src_c = (char *)src;
char *w = *dst;
while(length--)
*w++ = *src_c++;
*dst = w;
}
void packet_append_n16(char **dst,net_16 x)
{
packet_append_bytes(dst,&x,2);
}
// Send must construct an entire ethernet packet
// for whatever protocol it is sending.
// The address can be deduced from the packet
// Note -- the adapter's send routine
// is not reentrant, so it should disable
// interrupts until it has made its
// best attempt to send.
// Note -- In the case of an ip packet,
// this routine can end up recursively
// calling itself as part of the ARP exchange.
// (The nr_plugs_ip_to_ethernet routine will need
// to send an arp request the first time an
// ip address is encountered.) (If the adapter
// requires mac address.) Usually we'll have
// the address in cache already, though, from the
// incoming ip message.
// plug_handle -- if this is a small integer,
// then it is presumed to be just a protocol
// number. This is so we can send a packet
// without a plug allocated, in some cases.
// Specifically, this lets us send tcp rst
// messages in response to spurious packets.
// If used like this, the adapter index must
// be coded into the low flags bits.
int nr_plugs_send_to(int plug_handle,
void *payload,
int payload_length,
long flags,
net_32 ip_address,
net_16 port)
{
s_plugs_globals *g = &ng_plugs_globals;
int result = 0;
ns_plugs_ethernet_packet *ep;
char ep_contents[1500]; // this should be supplied by each adapter
char *ep_w; // progressive walk along the packet...
int plug_protocol;
net_16 plug_local_port;
int adapter_index;
s_plugs_adapter_entry *a;
// |
// | Extract the protocol, flags, and source port
// | either from the plug pointer, or inferentially
// | from the (fake) plug_handle
// |
if(plug_handle < ne_plugs_last_protocol) // not a plug handle!
{
plug_protocol = plug_handle;
plug_local_port = 0;
plug_handle = -1; // so debug printing reveals the fake plug-ness
adapter_index = flags & ne_plugs_flag_adapter_index_mask;
}
else
{
s_plugs_plug *plug;
plug_handle -= nk_plugs_plug_number_base;
plug = &g->plug[plug_handle];
plug_protocol = plug->protocol;
plug_local_port = plug->local_port;
flags |= plug->flags;
adapter_index = plug->flags & ne_plugs_flag_adapter_index_mask;
}
// |
// | Make sure the adapter index is reasonable,
// | and then get the adapter description into "a".
// |
if(adapter_index >= g->adapter_count)
{
result = ne_plugs_error_not_initialized;
goto go_home;
}
a = &g->adapter[adapter_index];
// Turn off interrupts while sending.
//
#if 0
PLUGS_IRQ
if (nr_plugs_interrupts_enabled())
(a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
0
);
#endif // PLUGS_IRQ
#if PLUGS_DEBUG
if(flags & ne_plugs_flag_debug_tx)
{
printf(" [plugs] +-----------------------\n");
printf(" [plugs] | plug %d (%s): sends %d byte payload\n",
plug_handle,
get_protocol_name(plug_protocol),
payload_length);
}
#endif // PLUGS_DEBUG
ep = (ns_plugs_ethernet_packet *)ep_contents;
ep_w = (char *)ep_contents;
if(plug_protocol == ne_plugs_ethernet)
{
// ethernet protocol means your "payload" is, in fact,
// the whole ethernet packet. 1st 14 bytes is the header.
packet_append_bytes(&ep_w,payload,payload_length);
}
else if(plug_protocol == ne_plugs_arp)
{
// arp protocol: payload is presumed to be
// a 28-byte arp packet. We don't even
// check the length.
ns_plugs_arp_packet *arp_packet;
net_48 broadcast_ethernet_address;
arp_packet = (ns_plugs_arp_packet *)payload;
// ethernet header
broadcast_ethernet_address.u32 = 0xffffFFFF;
broadcast_ethernet_address.l16 = 0xffff;
packet_append_bytes(&ep_w,
(flags & ne_plugs_flag_ethernet_broadcast)
? &broadcast_ethernet_address
: &arp_packet->target_ethernet_address,
6);
packet_append_bytes(&ep_w,&a->settings.ethernet_address,6);
packet_append_n16(&ep_w,nm_h2n16(ne_plugs_ethernet_arp));
// arp packet as delivered to us
packet_append_bytes(&ep_w,payload,28);
}
else if
(
plug_protocol == ne_plugs_ip
|| plug_protocol == ne_plugs_icmp
|| plug_protocol == ne_plugs_udp
|| plug_protocol == ne_plugs_tcp_raw
)
{
// | icmp, udp, tcp: payload is just the data to be
// | sent to the port and address specified in
// | the nr_plugs_connect call.
ns_plugs_ip_packet *ip_packet;
net_48 ep_ethernet_address;
// | look up ethernet destination
result = nr_plugs_ip_to_ethernet
(
adapter_index,
ip_address,
&ep_ethernet_address,flags
);
if(result)
goto go_home;
// |
// | ethernet header
packet_append_bytes(&ep_w,&ep_ethernet_address,6);
packet_append_bytes(&ep_w,&a->settings.ethernet_address,6);
packet_append_n16(&ep_w,nm_h2n16(ne_plugs_ethernet_ip));
// |
// | construct ip header in place (not appending)
ip_packet = (ns_plugs_ip_packet *)ep_w;
ep_w += sizeof(ns_plugs_ip_packet);
ip_packet->version_header_length = 0x45; // 0x45 for no options
ip_packet->tos = 0; // zero
ip_packet->length = nr_h2n16(sizeof(ns_plugs_ip_packet) + payload_length
+ ((plug_protocol == ne_plugs_udp)
? sizeof(ns_plugs_udp_packet) : 0)); // header + payload length
ip_packet->identifier = nr_h2n16(g->ip_identifier++); // bump for each packet
ip_packet->flags_fragment = nm_h2n16(0); // fragment miscellany
ip_packet->time_to_live = 128; // some typical value
ip_packet->protocol = (plug_protocol == ne_plugs_icmp) ? ne_plugs_ip_icmp :
(plug_protocol == ne_plugs_udp) ? ne_plugs_ip_udp :
ne_plugs_ip_tcp;
ip_packet->header_checksum = 0;
ip_packet->source_ip_address = a->settings.ip_address;
ip_packet->destination_ip_address = ip_address;
// |
// | maybe append udp header
if(plug_protocol == ne_plugs_udp)
{
int udp_packet_length = sizeof(ns_plugs_udp_packet) + payload_length;
packet_append_n16(&ep_w,plug_local_port);
packet_append_n16(&ep_w,port);
packet_append_n16(&ep_w,nr_n2h16(udp_packet_length));
packet_append_n16(&ep_w,0); // no stinking checksum
}
// |
// | append the payload
packet_append_bytes(&ep_w,payload,payload_length);
// |
// | After the whole packet is together,
// | work backwards out and do the checksums.
// | maybe calculate icmp checksum, so courteously!
if(plug_protocol == ne_plugs_icmp)
{
ns_plugs_icmp_packet *icmp_packet;
icmp_packet = (ns_plugs_icmp_packet *)ip_packet->payload;
// | clear checksum, first
icmp_packet->checksum = 0;
icmp_packet->checksum = r_calculate_checksum(ip_packet->payload,payload_length,0,0);
}
else if(plug_protocol == ne_plugs_udp)
{
ns_plugs_udp_packet *udp_packet = (ns_plugs_udp_packet *)ip_packet->payload;
int udp_packet_length = sizeof(ns_plugs_udp_packet) + payload_length;
udp_packet->checksum = r_calculate_checksum(
udp_packet,
udp_packet_length,
ip_packet,
udp_packet_length);
}
else if(plug_protocol == ne_plugs_tcp_raw
|| (flags & ne_plugs_flag_tcp_checksum))
{
ns_plugs_tcp_packet *tcp_packet = (ns_plugs_tcp_packet *)ip_packet->payload;
tcp_packet->checksum = 0;
tcp_packet->checksum = r_calculate_checksum(
tcp_packet,
payload_length,
ip_packet,
payload_length);
}
ip_packet->header_checksum = r_calculate_checksum(ip_packet,
sizeof(ns_plugs_ip_packet),0,0);
}
#if PLUGS_DEBUG
else
printf(" [plugs] | nr_plugs_send: unknown plug %d protocol %d\n",
plug_handle,plug_protocol);
#endif // PLUGS_DEBUG
#if PLUGS_DEBUG
if(flags & ne_plugs_flag_debug_tx)
nr_plugs_print_ethernet_packet(ep,ep_w - ep_contents," [plugs] | ");
#endif // PLUGS_DEBUG
// |
// | Send the packet to the hardware adapter
// |
{
int watchdog = 0; // we will try a few time
send_again:
// TPA: Before sending, check the mailbox.
nr_plugs_idle();
result = (a->adapter_description->tx_frame_proc)
(
a->adapter_address,
&a->adapter_storage,
ep_contents,
ep_w - ep_contents
);
// Positive, nonzero result means: retry.
if(result > 0 && watchdog < 20)
{
// failed to send for some reason?
// bump the watchdog and do it again
// (unless weve tried enough: just return the error)
watchdog++;
nr_plugs_idle();
nr_delay(2);
printf ("T");
goto send_again;
}
// Negative result means: "I hate you."
if (result < 0)
goto go_home;
}
go_home:
#if PLUGS_DEBUG
if(result)
{
if(flags & ne_plugs_flag_debug_tx)
printf(" [plugs] | ERROR nr_plugs_send_to result = %d\n",result);
}
#endif // PLUGS_DEBUG
// Turn on interrupts when done sending, if we're using
// interrupts.
#if 0
PLUGS_IRQ
if (nr_plugs_interrupts_enabled() &&
(adapter_index < g->adapter_count))
(a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
1
);
// Interrupts might have happened
// while we were off-line. Check.
nr_plugs_idle();
#endif //PLUGS_IRQ
return result;
}
int nr_plugs_send(int plug_handle,
void *payload,
int payload_length,
long flags)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_plug *plug;
int plugIndex;
int result;
plug = r_plug_from_handle(plug_handle);
// |
// | For a tcp connection, shunt it through
// | r_tcp_send (which eventually
// | calls through to nr_plugs_send_to,
// | after building the tcp packet &c.)
// |
#if PLUGS_TCP
if(plug->protocol == ne_plugs_tcp)
{
result = r_tcp_send
(
plug_handle,
plug,
ne_plugs_flag_tcp_ack | ne_plugs_flag_tcp_psh,
payload,
payload_length
);
}
else
#endif // PLUGS_TCP
{
result = nr_plugs_send_to(plug_handle,payload,payload_length,flags,
plug->remote_ip_address,
plug->remote_port);
}
return result;
}
// +------------------------
// | Mundane stuff, like setting
// | the LED and such
int nr_plugs_set_mac_led(int adapter_index,int led_onoff)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = &g->adapter[adapter_index];
return (a->adapter_description->set_led_proc)
(
a->adapter_address,
&a->adapter_storage,
led_onoff
);
}
int nr_plugs_set_mac_loopback(int adapter_index,int loopback_onoff)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = &g->adapter[adapter_index];
return (a->adapter_description->set_loopback_proc)
(
a->adapter_address,
&a->adapter_storage,
loopback_onoff
);
}
#if PLUGS_IRQ
void r_plugs_irq_handler(int context, int irq_unumber, int interruptee_pc)
{
int result; // gets lost
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = (s_plugs_adapter_entry *)context;
// | Store interruptee_pc in case some callback routine (debug)
// | needs to do something with it (put a trap there)
g->interruptee_pc = interruptee_pc<<1;
result = (a->adapter_description->check_for_events_proc)
(
a->adapter_address,
&a->adapter_storage,
nr_plugs_dispatch_packet,
a // context is adapter pointer
);
// | Clear this before leaving just to be neat
g->interruptee_pc = 0;
}
#endif
// +-----------------------------------
// | nr_plugs_get_interruptee_pc
// | Return the interruptee_pc stored
// | by the ISR above
int nr_plugs_get_interruptee_pc ()
{
s_plugs_globals *g = &ng_plugs_globals;
return (g->interruptee_pc);
}
// +-----------------------------------
// | nr_plugs_interrupts_enalbed
// | Return TRUE if plugs interrupts are enabled
// | otherwise false
int nr_plugs_interrupts_enabled ()
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = g->adapter;
return (a->adapter_irq);
}
void r_set_irqs_for_plug (s_plugs_plug* plug, int on_off)
{
s_plugs_adapter_entry *a;
s_plugs_globals *g = &ng_plugs_globals;
if (!nr_plugs_interrupts_enabled())
return;
a = &g->adapter[plug->flags & ne_plugs_flag_adapter_index_mask];
(a->adapter_description->set_irq_proc)
(
a->adapter_address,
&a->adapter_storage,
on_off
);
// Interrupts might have transpired while
// we were off-line. check.
if (on_off)
nr_plugs_idle();
}
// +-----------------------------------
// | nr_plugs_idle
// | Poll for events and other interesting things.
//
int nr_plugs_idle(void)
{
s_plugs_globals *g = &ng_plugs_globals;
int adapter_index;
s_plugs_adapter_entry *a;
int result = 0;
if(!g->running)
return ne_plugs_error_not_initialized; // | plugs not initialized
if(g->in_idle)
return ne_plugs_error;
// +--------------------
// | Can't call more than one deep for check_for_events, thank you.
// |
a = g->adapter;
for(adapter_index = 0; adapter_index < g->adapter_count; adapter_index++)
{
#if PLUGS_IRQ
if(!a->adapter_irq)
#endif // PLUGS_IRQ
{
g->in_idle++;
result = (a->adapter_description->check_for_events_proc)
(
a->adapter_address,
&a->adapter_storage,
nr_plugs_dispatch_packet,
a // context is adapter pointer
);
g->in_idle--;
}
a++;
}
return result;
}
int nr_plugs_idle_exclusive (int plug_handle)
{
s_plugs_globals *g = &ng_plugs_globals;
s_plugs_adapter_entry *a = g->adapter;
// | Save and clear adapter_irq so nr_plugs_idle call
// | will always check for events
int adapter_irq = a->adapter_irq;
a->adapter_irq = 0;
// | Set the exclusive plug
g->exclusive_plug_handle = plug_handle;
// | Now check for events
nr_plugs_idle ();
// | Clear the exclusive plug
g->exclusive_plug_handle = 0;
// | Reset the adapter_irq
a->adapter_irq = adapter_irq;
}
// end of file