ICMP запросы (Ping) на uIP

Стек uIP умеет только отвечать на ICMP запросы (если его пингуют, к примеру), но не умеет их отправлять. Исправим это.

Составляем IP заголовок

#define ICMPBUF ((struct uip_icmpip_hdr *)&uip_buf[UIP_LLH_LEN])
 
ICMPBUF->vhl = 0x45;
ICMPBUF->tos = 0;
uint16_t	len = sizeof(struct uip_icmpip_hdr);	// длина будет равна заголовку ip + icmp
ICMPBUF->len[0] = len >> 8;
ICMPBUF->len[1] = len & 0xff;
uint16_t	ipid = 0;
ICMPBUF->ipid[0] = ipid >> 8;
ICMPBUF->ipid[1] = ipid & 0xff;
ICMPBUF->ipoffset[0] = ICMPBUF->ipoffset[1] = 0;
ICMPBUF->ttl = UIP_TTL;
ICMPBUF->proto = UIP_PROTO_ICMP;
// указываем свой ip и ip удаленного устройства.
uip_gethostaddr(ICMPBUF->srcipaddr);
uip_ipaddr(&ICMPBUF->destipaddr, 192, 168, 1, 1);	// адрес, который будем пинговать
 
ICMPBUF->ipchksum = 0;
ICMPBUF->ipchksum = ~(uip_ipchksum());
 
ICMPBUF->type = 8; //ICMP_ECHO;
ICMPBUF->icode = 0;
ICMPBUF->icmpchksum = 0;
ICMPBUF->icmpchksum = uip_chksum((uint16_t*)&ICMPBUF->type, 8);
ICMPBUF->icmpchksum = ~((ICMPBUF->icmpchksum == 0) ? 0xffff : ICMPBUF->icmpchksum);
 
uip_len = UIP_IPH_LEN + 8;	// длина равна заголовку ip + заголовку icmp

Далее пуляем arp

uip_arp_out();


И отправляем пакет

enc28j60_send_packet((uint8_t *) uip_buf, uip_len);

Здесь надо учесть, что если текущего IP нет в ARP таблице, то на данном этапе будет отправлен ARP запрос. Чтобы понять, отправлен ARP или сформированный нами запрос, перепишем немного uip_arp_out

// Если запись в arp таблице не найдена, вернет 0. При этом данные в буффере будут заменены на arp запрос
uint8_t uip_arp_out(void)
{
	struct arp_entry *tabptr;
 
	/* Find the destination IP address in the ARP table and construct
     the Ethernet header. If the destination IP addres isn't on the
     local network, we use the default router's IP address instead.
 
     If not ARP table entry is found, we overwrite the original IP
     packet with an ARP request for the IP address. */
 
	// First check if destination is a local broadcast.
	if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr)) {
		memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
	} else {
		// Check if the destination address is on the local network.
		if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask)) {
			/* Destination address was not on the local network, so we need to
	 use the default router's IP address instead of the destination
	 address when determining the MAC address. */
			uip_ipaddr_copy(ipaddr, uip_draddr);
		} else
			// Else, we use the destination IP address.
			uip_ipaddr_copy(ipaddr, IPBUF->destipaddr);
 
		for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { tabptr = &arp_table[i]; if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) break;
		}
 
		if(i == UIP_ARPTAB_SIZE) {
			// The destination address was not in our ARP table, so we overwrite the IP packet with an ARP request.
 
			memset(BUF->ethhdr.dest.addr, 0xff, 6);
			memset(BUF->dhwaddr.addr, 0x00, 6);
			memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
			memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);
 
			uip_ipaddr_copy(BUF->dipaddr, ipaddr);
			uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);
			BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */
			BUF->hwtype = HTONS(ARP_HWTYPE_ETH);
			BUF->protocol = HTONS(UIP_ETHTYPE_IP);
			BUF->hwlen = 6;
			BUF->protolen = 4;
			BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
 
			uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];
 
			uip_len = sizeof(struct arp_hdr);
			return 0;
		}
 
		// Build an ethernet header.
		memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
	}
	memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
 
	IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);
 
	uip_len += sizeof(struct uip_eth_hdr);
	return 1;
}

Готово!

Leave a Reply