Trong bài trước, chúng ta đã tìm hiểu về giao thức ARP và đã có thể bắt được cái gói tin ARP request được gửi tới. Và ở bài này, chúng ta sẽ viết chương trình để trả lời các ARP reques đó ( ARP reponse)
Các gói tin ARP sẽ được xử lí ở thư viện ARP, nên mình sẽ tạo thêm 2 file arp.c và arp.h và add vào project như đã nói ở các bài trước ( vào Project -> Configure để add file arp.c )
Ở file arp.h các bạn copy khối code cơ bản
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef ARP_H_ #define ARP_H_ //-------------------------------------------------- //Include cac thu vien can thiet #include <mega328p.h> #include <delay.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <spi.h> #include "uart.h" #include "net.h" //------------------------------------------------- //-------------------------------------------------- #endif /* ARP_H_ */ |
Tương tự với file arp.c
1 2 3 4 5 6 7 |
#include "arp.h" //-------------------------------------------------------------------------- //End file |
Trong file net.h các bạn add thêm thư viện arp.h vô
Đồng thời trong file net.c , bỏ comment cho hàm ARP_read_packet vì chúng ta sẽ viết hàm này ngay bây giờ
Giờ mình mới để ý lời gọi hàm ARP_read_packet trong file net.c mình viết sai. Các bạn sửa lại cho đúng nhé ! ARP_read_packet(net_buffer,len);
Tạo hàm ARP_read_packet trong file arp.c và khai báo nguyên mẫu hàm cho nó vào file arp.h
1 2 3 4 |
void ARP_read_packet(uint8_t *ARP_Buff,uint16_t len) { } |
Chúng ta đã nhận được 1 gói tin ARP, công việc bây giờ là :
- Kiểm tra xem gói tin ARP này thuộc loại gì: ARP request hay ARP reponse bằng cách check trường Opcode tại vị trí byte thứ 20 21
- Nếu đó là ARP request thì phải chuẩn bị gói tin trả lời lại
- Nếu đó là ARP reponse, kiểm tra địa chỉ MAC đích xem có đúng là nó reponse chi mình không, nếu không trùng MAC của mình thì bỏ qua. Nếu đúng là nó gửi cho mình thì tiến hành lưu MAC của thằng đã reponse vào bảng để sử dụng
Chà ! Khối lượng công việc khá nhiều đây, chúng ta sẽ làm dần từng ý một
Bây giờ mình sẽ gán cho chip của chúng ta 1 địa chỉ IP, do IP này là IP cố định dễ có khả năng trùng IP với thiết bị trong mạng, nên mình sẽ lấy 1 IP có giá trị cao 1 chút 192.168.1.197 đi
Các bạn click chuột vào con chip ENC28J60 trong mô phỏng, điền IP mà mình gán cho nó vào
Tương tự, trong file enc28j60.c, cạnh chỗ khai báo MAC, mình cũng khai báo IP 192.168.1.197 vô
1 |
const uint8_t ip [4] = {192,168,1,197}; |
Và khái báo lại ip với mac ở các file thư viện .c khác bằng cú pháp extern
1 2 3 |
extern const uint8_t macaddr[6]; extern const uint8_t ip[4]; extern uint8_t debug_string[60]; |
Tiếp tục với nội dung hàm ARP_read_packet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void ARP_read_packet(uint8_t *ARP_Buff,uint16_t len) { //tao 2 array de luu IP và MAC cua nguon uint8_t MAC_source[6]; uint8_t IP_source[4]; uint8_t IP_target[4]; memcpy(MAC_source,(uint8_t *)&ARP_Buff[6],6); //lay MAC cua nguon gui packet memcpy(IP_source ,(uint8_t *)&ARP_Buff[28],4); //lay IP cua nguon gui packet memcpy(IP_target ,(uint8_t *)&ARP_Buff[38],4); //lay IP cua nguon nhan packet if(ARP_Buff[20] == 0x00 && ARP_Buff[21] == 0x01) //neu Opcode = 0x0001 thi day la ban tin ARP request { //kiem tra xem co trung voi IP cua minh khong if(IP_target[0] != ip[0] || IP_target[1] != ip[1] || IP_target[2] != ip[2] || IP_target[3] != ip[3] ) return; UART_putString("Da nhan 1 arp request\r\n"); } } |
Giải thích: Sau khi lấy được các thông tin trong gói arp, chúng ta kiểm tra Opcode xem nó có phải là arp request không, nếu đúng tiếp tục kiểm tra xem ARP request này có ip đích trùng với ip của mình không ! nếu không trùng thì bỏ qua ! Nếu đúng thì sẽ in ra đong debug là đã nhận được 1 arp request
Chúng ta sẽ test thử xem code đã hoạt động chưa nhé. Mình sẽ dùng máy tính để gửi 1 arp request tới ip của enc28j60
Trước đó mình sẽ commnet mấy dòng debug (UART_putString) ở các file enc28j60.c và net.c đi vì chúng ta đã test các hàm đó hoạt động rồi, in ra lắm cũng ngứa mắt
Bây giờ mình sẽ dùng phần mềm herculer, chức năng UDP để cho nó gửi thử 1 arp request xem chíp có bắt được không nhé !
Các bạn gửi thử 1 nội dung bất kì, phần mềm herculer sẽ tự động gửi request tới ip đã điền
OK, chúng ta đã bắt được arp request
Bây giờ mình sẽ viết thêm code để phản hồi lại nhé
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
void ARP_read_packet(uint8_t *ARP_Buff,uint16_t len) { struct { uint8_t MAC_dich[6]; // MAC dich uint8_t MAC_nguon[6]; //MAC nguon uint16_t Ethernet_type; //ethernet type uint16_t Hardwave_type; //hardwave type uint16_t Protocol_type; //protocol type (ARP) uint16_t Size; //size uint16_t Opcode; //opcode uint8_t MAC_sender[6]; //sender MAC uint8_t IP_sender[4]; //sender IP uint8_t MAC_target[6]; // Target MAC uint8_t IP_target[4]; // Target IP }ARP_reponse; //tao 2 array de luu IP và MAC cua nguon uint8_t MAC_source[6]; uint8_t IP_source[4]; uint8_t IP_target[4]; memcpy(MAC_source,(uint8_t *)&ARP_Buff[6],6); //lay MAC cua nguon gui packet memcpy(IP_source ,(uint8_t *)&ARP_Buff[28],4); //lay IP cua nguon gui packet memcpy(IP_target ,(uint8_t *)&ARP_Buff[38],4); //lay IP cua nguon nhan packet if(ARP_Buff[20] == 0x00 && ARP_Buff[21] == 0x01) //neu Opcode = 0x0001 thi day la ban tin ARP request { //kiem tra xem co trung voi IP cua minh khong if(IP_target[0] != ip[0] || IP_target[1] != ip[1] || IP_target[2] != ip[2] || IP_target[3] != ip[3] ) return; UART_putString("Da nhan 1 arp request\r\n"); //tao goi tin ARP reponse memcpy(ARP_reponse.MAC_dich,MAC_source,6); //dia chi MAC cua thang nhan goi tin reponse memcpy(ARP_reponse.MAC_target,MAC_source,6); memcpy(ARP_reponse.IP_target,IP_source,4); //dia chi IP cua thang nhan goi tin reponse memcpy(ARP_reponse.MAC_nguon,macaddr,6); //dia chi MAC cua thang gui ( chinh la MAC cua ENC28J60) memcpy(ARP_reponse.MAC_sender,macaddr,6); memcpy(ARP_reponse.IP_sender,ip,4); //dia chi IP cua thang gui goi tin reponse ( chinh la MAC cua ENC28J60) ARP_reponse.Ethernet_type=0x0608; ARP_reponse.Hardwave_type=0x0100; ARP_reponse.Protocol_type=0x0008; ARP_reponse.Size=0x0406; ARP_reponse.Opcode=0x0200; UART_putString("Gui gon tin reply\r\n"); NET_SendFrame((uint8_t *)&ARP_reponse,42); //gui goi ARP reponse di } } |
Giải thích:
Mình tạo 1 Struct theo cấu trúc của 1 gói tin trong giao thức ARP, sau đó điền các thông tin như địa chỉ MAC đích, Mac nguồn, IP đích, IP nguồn và gọi hàm NET_SendFrame để gửi tra lại gói tin ARP reponse
Và giờ, WireShark đã bắt được bản tin ARP reponse của ENC28J60
Thật tuyệt, tới thời điểm bây giờ chúng ta đã bắt được 1 gói tin ARP request và phản hồi lại cho thằng request 1 bản tin ARP reponse
Trong bài tiếp theo, chúng ta sẽ tự gửi đi 1 ARP request và bắt lại gói tin ARP reponse và thiết kế bảng lưu địa chỉ MAC cho giao thức ARP
Các bạn có thể tải source cho bài này ở đây
Khi gửi ARP request cho ENC28 hàm chạy không ổn định, hầu như không nhảy vào ngắt