📄 ip.c
字号:
//-----------------------------------------------------------------------------
// Copyright (c) 2002 Jim Brady
// Do not use commercially without author's permission
// Last revised August 2002
// Net IP.C
//
// This module is the IP layer
// Refer to RFC 791, 1122, and RFC 815 (fragmentation)
//-----------------------------------------------------------------------------
#include "stdlib.h"
#include "net.h"
#include "cksum.h"
#include "arp.h"
#include "icmp.h"
#include "udp.h"
#include "eth.h"
#include "tcp.h"
#include "ip.h"
#include "utils.h"
extern UCHAR debug;
extern ulong my_ipaddr;
extern UCHAR broadcast_hwaddr[];
WAIT volatile wait;
//------------------------------------------------------------------------
// This handles outgoing IP datagrams. It adds the 20 byte IP header
// and checksum then forwards the IP datagram to the Ethernet layer
// for sending. See "TCP/IP Illustrated, Volume 1" Sect 3.2
//------------------------------------------------------------------------
void ip_send(UCHAR * outbuf, ulong ipaddr, UCHAR proto_id, uint len)
{
//UCHAR testadd[6]={0x00,0x19,0x21,0x5b,0xd8,0x3f};
IP_HEADER * ip;
UCHAR * hwaddr;
static uint ip_ident = 0;
ip = (IP_HEADER *)(outbuf + 14);
ip->ver_len = 0x45; //版本(=$4) + 首部长度(= $5 * 4 =20): IPv4 with 20 byte header
ip->type_of_service = 0; //服务类型:
ip->total_length = 20 + len; //总长度: 20 + len
ip->identifier = ip_ident++; //标识号: sequential identifier
ip->fragment_info = 0; //标志: 本案为不能分片; not fragmented
ip->time_to_live = 128; //生存时间: max hops
ip->protocol_id = proto_id; //上层协议: type of payload
ip->header_cksum = 0; //首部校验和:
ip->source_ipaddr = my_ipaddr;
// Outgoing IP address
ip->dest_ipaddr = ipaddr;
// Compute and insert complement of checksum of ip header
// Outgoing ip header length is always 20 bytes
ip->header_cksum = ~cksum(outbuf + 14, 20);
// Use ARP to get hardware address to send this to 通过ARP表取得目的主机的MAC地址;
hwaddr = arp_resolve(ip->dest_ipaddr);
// Null means that the ARP resolver did not find the IP address
// in its cache so had to send an ARP request
#ifdef __LITTLEENDIAN__
// hwaddr = testadd;
#endif
//没找到此IP地址对应MAC地址, 把欲发送的缓冲区信息和地址和端口等放入wait结构里,等找到时再发送
if (hwaddr == NULL)
{ // Fill in the destination information so ehrn the ARP response
// arrives we can identify it and know what to do when we get it
if(ip->dest_ipaddr == 0xffffffff)
{
hwaddr = broadcast_hwaddr;
}
else
{
wait.buf = outbuf;
wait.ipaddr = ip->dest_ipaddr;
wait.proto_id = proto_id;
wait.len = len;
wait.timer = ARP_TIMEOUT;
return;
}
}
#ifdef __LITTLEENDIAN__
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
ip->source_ipaddr = ntohl(ip->source_ipaddr);
ip->total_length = ntohs(ip->total_length);
ip->identifier = ntohs(ip->identifier);
ip->header_cksum = 0;
ip->header_cksum = ~cksum(outbuf + 14, 20);
ip->header_cksum = ntohs(ip->header_cksum);
#endif
eth_send(outbuf, hwaddr, IP_PACKET, 20 + len);
}
//------------------------------------------------------------------------
// This handles incoming IP datagrams from the Ethernet layer
// See "TCP/IP Illustrated, Volume 1" Sect 3.2
//------------------------------------------------------------------------
void ip_rcve(UCHAR * inbuf)
{
IP_HEADER * ip;
uint header_len, payload_len;
ip = (IP_HEADER *)(inbuf + 14);
// Make sure it is addressed to my IP address
//目的IP地址不匹配, 返回
#ifdef __LITTLEENDIAN__
ip->total_length = ntohs(ip->total_length);
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
#endif
if (ip->dest_ipaddr != my_ipaddr)
return;
// Validate checksum of ip header
header_len = 4 * (0x0F & ip->ver_len); //计算首部长度
payload_len = ip->total_length - header_len; //计算静负荷长度
#ifdef __LITTLEENDIAN__
ip->total_length = ntohs(ip->total_length);
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
#endif
if (cksum(inbuf + 14, header_len) != 0xFFFF) //计算首部校验和, 不等于0xFFFF就丢弃;
{
//if (debug) serial_send("IP: Error, cksum bad\r");
return;
}
#ifdef __LITTLEENDIAN__
ip->total_length = ntohs(ip->total_length);
ip->fragment_info = ntohs(ip->fragment_info);
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
ip->source_ipaddr = ntohl(ip->source_ipaddr);
ip->header_cksum = ntohs(ip->header_cksum);
#endif
// Make sure incoming message is IP version 4
if ((ip->ver_len >> 4) != 0x04) //不支持非版本4
{
//if (debug) serial_send("IP: Error, not IPv4\r");
return;
}
// Make sure incoming message is not fragmented because
// we cannot handle fragmented messages
// 不支持分片IP包;
if ((ip->fragment_info & 0x3FFF) != 0)
{
//if (debug) serial_send("IP: Error, fragmented msg rcvd\r");
return;
}
// At this point we have received a valid IP datagram addressed
// to me. We do not use header options, and do not forward
// messages, so in the unlikely event there are header options,
// delete them and shift the data down. The advantage is that
// layers such as UDP and TCP know where their data starts
//如果首部长度 > 20, 即存在可变部分, 把净负荷数据移到从固定部分20字节开始.
if (header_len > 20)
{
//if (debug) serial_send("IP: Rcvd header > 20 bytes\r");
// Use memmove because of overlap
// memmove(inbuf + 34, inbuf + 14 + header_len, payload_len);
memcpy(inbuf + 34, inbuf + 14 + header_len, payload_len);
// Adjust info to reflect the move
header_len = 20;
ip->ver_len = 0x45;
ip->total_length = 20 + payload_len;
}
// Look at protocol ID byte and call the appropriate
// function to handle the received message. See
// "TCP/IP Illustrated, Volume 1" Sect 1.7 and RFC 791
// for values for various protocols
//按上层协议号,分别提交给上层不同协议进程处理;
switch (ip->protocol_id)
{
case ICMP_TYPE:
//if (debug)
// serial_send("IP: ICMP pkt rcvd\n");
icmp_rcve(inbuf, payload_len);
break;
case IGMP_TYPE:
// We cannot handle IGMP messages
//if (debug)
// serial_send("IP: Error, IGMP pkt rcvd\n");
break;
case UDP_TYPE:
//if (debug)
#ifdef __LITTLEENDIAN__
ip->total_length = ntohs(ip->total_length);
ip->fragment_info = ntohs(ip->fragment_info);
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
ip->source_ipaddr = ntohl(ip->source_ipaddr);
ip->header_cksum = ntohs(ip->header_cksum);
#endif
// serial_send("IP: UDP pkt rcvd\n");
udp_rcve(inbuf, payload_len);
break;
case TCP_TYPE:
// if (debug)
// serial_send("IP: TCP pkt rcvd\n");
#ifdef __LITTLEENDIAN__
ip->total_length = ntohs(ip->total_length);
ip->fragment_info = ntohs(ip->fragment_info);
ip->dest_ipaddr = ntohl(ip->dest_ipaddr);
ip->source_ipaddr = ntohl(ip->source_ipaddr);
ip->header_cksum = ntohs(ip->header_cksum);
#endif
tcp_rcve(inbuf, payload_len);
break;
default:
// if (debug) serial_send("IP: Unknown IP proto id rcvd\r");
break;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -