【计网实验——prj9】路由器转发实验
实验要求
实验内容一
- 运行给定网络拓扑(router_topo.py)
- 在r1上执行路由器程序./router,进行数据包的处理
- 在h1上进行ping实验
- Ping 10.0.1.1 (r1),能够ping通
- Ping 10.0.2.22 (h2),能够ping通
- Ping 10.0.3.33 (h3),能够ping通
- Ping 10.0.3.11,返回ICMP Destination Host Unreachable
- Ping 10.0.4.1,返回ICMP Destination Net Unreachable
实验内容二
- 构造一个包含多个路由器节点组成的网络
- 手动配置每个路由器节点的路由表
- 有两个终端节点,通过路由器节点相连,两节点之间的跳数不少于3跳,手动配置其默认路由表
- 连通性测试
- 终端节点ping每个路由器节点的入端口IP地址,能够ping通
- 路径测试
- 在一个终端节点上traceroute另一节点,能够正确输出路径上每个节点的IP信息
实现方案
路由器的实现主要分为ARP和IP两个层次的数据包的处理和转发。
1. ARP数据包处理
路由器在路由表中查找到下一跳IP地址后,需要得到IP地址对应的MAC地址才能实现局域网内数据包的转发。路由器使用ARP机制来查询下一跳IP地址对应的MAC地址,维护一个缓存ARP相关内容的数据结构——arpcache
。arpcache
中缓存IP->MAC映射条目和查找不到IP对应映射条目而等待ARP应答的数据包。
1)ARP缓存管理
在ARP机制中,路由器转发数据包之前,先在arpcache
中查找下一跳IP地址对应的IP->MAC映射条目,如果找到,则填充数据包的目的MAC地址,并转发数据包;否则,将该数据包缓存在arpcache->req_list中,并发送ARP请求。
arpcache.c
中 arpcache_lookup
函数
代码中用 arpcache_lookup
函数实现查找ARP缓存中的IP->MAC映射条目的过程,即遍历ARP缓存中的映射表,查找是否存在与输入IP地址匹配的条目,若存在则返回1,否则返回0。
// lookup the IP->mac mapping
//
// traverse the table to find whether there is an entry with the same IP
// and mac address with the given arguments
int arpcache_lookup(u32 ip4, u8 mac[ETH_ALEN])
{
fprintf(stderr, "TODO: lookup ip address in arp cache.\n");
pthread_mutex_lock(&arpcache.lock);
for(int i = 0; i < MAX_ARP_SIZE; i++){
if(ip4 == arpcache.entries[i].ip4 && arpcache.entries[i].valid){
memcpy(mac, arpcache.entries[i].mac, ETH_ALEN);
pthread_mutex_unlock(&arpcache.lock);
return 1;
}
}
pthread_mutex_unlock(&arpcache.lock);
return 0;
}
arpcache.c
中 arpcache_append_packet
函数
arpcache_append_packet
函数实现的是在ARP缓存映射表中查找不到目的IP地址对应的映射条目时,将该数据包缓存在arpcache->req_list中,并发送ARP请求的过程。首先需要先查找需要发送的目的IP地址在不在缓存队列中。如果不在,则分配一个空间用来记录需要请求的目的IP地址条目,并配置好相关参数。接着发送ARP请求(这可能已经不是第一次发送请求了),来获得目的IP对应的MAC地址。
代码实现如下:
// append the packet to arpcache
//
// Lookup in the list which stores pending packets, if there is already an
// entry with the same IP address and iface (which means the corresponding arp
// request has been sent out), just append this packet at the tail of that entry
// (the entry may contain more than one packet); otherwise, malloc a new entry
// with the given IP address and iface, append the packet, and send arp request.
void arpcache_append_packet(iface_info_t *iface, u32 ip4, char *packet, int len)
{
fprintf(stderr, "TODO: append the ip address if lookup failed, and send arp request if necessary.\n");
pthread_mutex_lock(&arpcache.lock);
struct arp_req *req_entry = NULL, *q;
int found_same_ip_entry = 0;
list_for_each_entry_safe(req_entry, q, &(arpcache.req_list), list){
if(req_entry->ip4 == ip4){
int found_same_ip_entry = 1;
break;
}
}
if(found_same_ip_entry == 0){
req_entry = (struct arp_req *) safe_malloc(sizeof(struct arp_req));
init_list_head(&(req_entry->list));
req_entry->ip4 = ip4;
init_list_head(&(req_entry->cached_packets));
req_entry->iface = (iface_info_t *) safe_malloc(sizeof(iface_info_t));
memcpy(req_entry->iface, iface, sizeof(iface_info_t));
req_entry->sent = 0;
req_entry->retries = 0;
list_add_tail(&(req_entry->list), &(arpcache.req_list));
}
struct cached_pkt *pkt = (struct cached_pkt *) safe_malloc(sizeof(struct cached_pkt));
pkt->packet = packet;
pkt->len = len;
init_list_head(&(pkt->list));
list_add_tail(&(pkt->list), &(req_entry->cached_packets));
//send arp request
arp_send_request(iface, ip4);
req_entry->retries ++;
req_entry->sent = time(NULL);
pthread_mutex_unlock(&arpcache.lock);
}
arpcache.c
中 arpcache_insert
函数
若端口收到ARP应答包,则说明该端口需要将源IP和源MAC地址的映射插入到arpcache
中,即调用arpcache_insert
处理。arpcache_insert
函数需要将新的IP->MAC的映射写入到arpcache
中valid
值为0的条目,如果缓存已满,则随机替换掉其中一个(在本实现中,直接在遍历所有条目的最后将最后一个条目替换为新的IP->MAC映射,将遍历和替换操作合并在一个循环中)。然后将在缓存中等待该映射的数据包,依次填充目的MAC地址,调用 arp.c
中的iface_send_packet_by_arp
函数转发出去,并删除掉相应缓存数据包。
代码实现如下:
// insert the IP->mac mapping into arpcache, if there are pending packets
// waiting for this mapping, fill the ethernet header for each of them, and send
// them out
void arpcache_insert(u