blob: e77466e13b003c028016c76a120b6ccdaf5c6d61 [file]
/*
* iphelp.cpp - ip router
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* Windows platform specific code copyright (C) Lauri Pesonen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This 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 program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include "main.h"
#include "cpu_emulation.h"
#include "ether_windows.h"
#include "ether.h"
#include "router.h"
#include "router_types.h"
#include "tcp.h"
#include "icmp.h"
#include "udp.h"
#include "iphelp.h"
#include "dump.h"
#if DEBUG
#pragma optimize("",off)
#endif
#include "debug.h"
void make_icmp_checksum( icmp_t *icmp, int len )
{
icmp->checksum = 0;
uint16 sz = (len-sizeof(ip_t))/2;
uint16 *p = (uint16 *)( (uint8 *)icmp + sizeof(ip_t) );
uint32 sum32 = 0;
for( int i=0; i<sz; i++ ) {
sum32 += ntohs(p[i]);
}
while( HIWORD(sum32) ) {
sum32 = HIWORD(sum32) + LOWORD(sum32);
}
sum32 = ~sum32;
icmp->checksum = htons((uint16)sum32);
}
void make_tcp_checksum( tcp_t *tcp, int len )
{
tcp->checksum = 0;
int tcp_len = len - sizeof(ip_t);
uint16 sz = tcp_len/2;
uint16 *p = (uint16 *)( (uint8 *)tcp + sizeof(ip_t) );
uint32 sum32 = 0;
for( int i=0; i<sz; i++ ) {
sum32 += ntohs(p[i]);
}
if(len & 1) {
uint8 *p8 = (uint8 *)p;
sum32 += p8[tcp_len-1] << 8;
}
pseudo_ip_t pseudo;
pseudo.src_lo = LOWORD(ntohl(tcp->ip.src));
pseudo.src_hi = HIWORD(ntohl(tcp->ip.src));
pseudo.dest_lo = LOWORD(ntohl(tcp->ip.dest));
pseudo.dest_hi = HIWORD(ntohl(tcp->ip.dest));
pseudo.proto = (uint16)tcp->ip.proto;
pseudo.msg_len = tcp->header_len >> 2;
int datalen = len - sizeof(tcp_t);
pseudo.msg_len += datalen;
p = (uint16 *)&pseudo;
for( int i=0; i<sizeof(pseudo_ip_t)/2; i++ ) {
sum32 += p[i];
}
while( HIWORD(sum32) ) {
sum32 = HIWORD(sum32) + LOWORD(sum32);
}
sum32 = ~sum32;
tcp->checksum = htons((uint16)sum32);
}
void make_ip4_checksum( ip_t *ip )
{
ip->checksum = 0;
uint16 sz = ip->header_len * 2;
uint16 *p = (uint16 *)( (uint8 *)ip + sizeof(mac_t) );
uint32 sum32 = 0;
for( int i=0; i<sz; i++ ) {
sum32 += ntohs(p[i]);
}
while( HIWORD(sum32) ) {
sum32 = HIWORD(sum32) + LOWORD(sum32);
}
sum32 = ~sum32;
ip->checksum = htons((uint16)sum32);
}
void make_udp_checksum( udp_t *udp )
{
udp->checksum = 0;
return;
// UDP checksums are optional.
/*
uint16 sz = ntohs(udp->msg_len) / 2;
uint16 *p = (uint16 *)( (uint8 *)udp + sizeof(ip_t) );
uint32 sum32 = 0;
for( int i=0; i<sz; i++ ) {
sum32 += ntohs(p[i]);
}
// last byte??
pseudo_ip_t pseudo;
pseudo.src_lo = LOWORD(ntohl(udp->ip.src));
pseudo.src_hi = HIWORD(ntohl(udp->ip.src));
pseudo.dest_lo = LOWORD(ntohl(udp->ip.dest));
pseudo.dest_hi = HIWORD(ntohl(udp->ip.dest));
pseudo.proto = (uint16)udp->ip.proto;
pseudo.msg_len = ntohs(udp->msg_len); // ???
p = (uint16 *)&pseudo;
for( i=0; i<sizeof(pseudo_ip_t)/2; i++ ) {
sum32 += p[i];
}
while( HIWORD(sum32) ) {
sum32 = HIWORD(sum32) + LOWORD(sum32);
}
sum32 = ~sum32;
udp->checksum = htons((uint16)sum32);
*/
}
void error_winsock_2_icmp( int err, ip_t *ip_err, int dlen_err )
{
int type = -1, code = -1, msg_size = 0;
switch( err ) {
case WSAEHOSTUNREACH:
case WSAETIMEDOUT:
type = icmp_Destination_unreachable;
code = 1; // Host unreachable
msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits
break;
case WSAENETDOWN:
case WSAENETUNREACH:
type = icmp_Destination_unreachable;
code = 0; // Network unreachable
msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits
break;
case WSAETTLEXCEEDED:
type = icmp_Time_exceeded;
code = 0; // Transit TTL exceeded
msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits
break;
}
if(type >= 0 && macos_ip_address != 0) {
D(bug("sending icmp error reply. type=%d, code=%d, msg_size=%d\r\n", type, code, msg_size));
int icmp_size = sizeof(icmp_t) + msg_size;
icmp_t *icmp = (icmp_t *)malloc( icmp_size );
if(icmp) {
mac_t *mac = (mac_t *)icmp;
ip_t *ip = (ip_t *)icmp;
memcpy( mac->dest, ether_addr, 6 );
memcpy( mac->src, router_mac_addr, 6 );
mac->type = htons(mac_type_ip4);
ip->version = 4;
ip->header_len = 5;
ip->tos = 0;
ip->total_len = htons(sizeof(icmp_t) - sizeof(mac_t) + msg_size);
ip->ident = htons(next_ip_ident_number++);
ip->flags_n_frag_offset = 0;
ip->ttl = 128;
ip->proto = ip_proto_icmp;
ip->src = htonl(router_ip_address);
ip->dest = htonl(macos_ip_address);
make_ip4_checksum( ip );
icmp->type = type;
icmp->code = code;
// zero out the unused field
memset( (char *)icmp + sizeof(icmp_t), 0, sizeof(uint32) );
// copy 64 bits of original message
memcpy(
(char *)icmp + sizeof(icmp_t) + sizeof(uint32),
(char *)ip_err + sizeof(mac_t),
msg_size
);
make_icmp_checksum( icmp, icmp_size );
dump_bytes( (uint8 *)icmp, icmp_size );
enqueue_packet( (uint8 *)icmp, icmp_size );
free(icmp);
}
}
}