강좌 & 팁
안녕하세요.
유형석입니다.
이번시간에는 이더넷 패킷을 받아서 출력해 봅시다!
1. 주의
저번시간에 설명 드렸던것 다시 한번 주의 하시라고 적어 봤습니다~
일단 저는 회사 보드의 부트로더에서 작업을 했습니다.
커널로 진입한뒤 어플로 실행하면 여러가지 간섭도 발생하기 때문에
부트로더에서 테스트를 하는게 더 좋을 것이라고 생각되네요.
그리고 참고 하실점은 이더넷 디바이스 드라이버로 쓰는 인터페이스 함수가 필요한데요.
이건 디바이스 드라이버 작성한 사람에 따라 틀려질수 있으니 유의하시길 바랍니다.
뭐 그렇다고 해도 왠만하면 가능하다고 봅니다.
일단 상황은 fd_eth_main 이라는 변수가 디바이스 드라이버에 등록되 있는 상태를 가정으로 진행 합니다.
2. 진행
struct _eth_test_packet { ETH_HDR eth_hdr; ETHARP_HDR etharp_hdr; }__attribute__((packed)); typedef struct _eth_test_packet ETH_TEST_PACKET;
그리고 나서 일단 소스가 나오겠습니다.
char buff[2048]; ETH_TEST_PACKET *test_packet = NULL; test_packet = (ETH_TEST_PACKET *)buff; int rx_size; while(1) { memset(test_packet, 0, sizeof((ETH_TEST_PACKET *)test_packet)); memset(buff, 0, sizeof(buff) ); rx_size = read( fd_eth_main, buff, sizeof( buff ) ); if( rx_size < 0 ) { printf( "fd_eth_main read error\n" ); break; } if( rx_size == 0 ) continue; printf("Ethernet header:\n"); printf("+-------------------+\n"); printf("| %02X:%02X:%02X:%02X:%02X:%02X | (dest)\n", test_packet->eth_hdr.dest[0], test_packet->eth_hdr.dest[1], test_packet->eth_hdr.dest[2], test_packet->eth_hdr.dest[3], test_packet->eth_hdr.dest[4], test_packet->eth_hdr.dest[5]); printf("+-------------------+\n"); printf("| %02X:%02X:%02X:%02X:%02X:%02X | (src)\n", test_packet->eth_hdr.src[0], test_packet->eth_hdr.src[1], test_packet->eth_hdr.src[2], test_packet->eth_hdr.src[3], test_packet->eth_hdr.src[4], test_packet->eth_hdr.src[5]); printf("+-------------------+\n"); printf("| %04X | ", htons(test_packet->eth_hdr.type)); printf("\n"); if(htons(test_packet->eth_hdr.type) == ETHTYPE_ARP) { printf("EthernetARP header:\n"); printf("+-------------------+\n"); printf("| %04X | (hwtype)\n", htons(test_packet->etharp_hdr.hwtype)); printf("+-------------------+\n"); printf("| %04X | (proto)\n", test_packet->etharp_hdr.proto); printf("+-------------------+\n"); printf("| %02X | (hwlen)\n", test_packet->etharp_hdr.hwlen); printf("+-------------------+\n"); printf("| %02X | (protolen)\n", test_packet->etharp_hdr.protolen); printf("+-------------------+\n"); printf("| %04X | (opcode)\n", htons(test_packet->etharp_hdr.opcode)); printf("+-------------------+\n"); printf("| %02X:%02X:%02X:%02X:%02X:%02X | (src MAC)\n", test_packet->etharp_hdr.shwaddr.addr[0], test_packet->etharp_hdr.shwaddr.addr[1], test_packet->etharp_hdr.shwaddr.addr[2], test_packet->etharp_hdr.shwaddr.addr[3], test_packet->etharp_hdr.shwaddr.addr[4], test_packet->etharp_hdr.shwaddr.addr[5]); printf("+-------------------+\n"); printf("| %3hu.%3hu.%3hu.%3hu | (src IP)\n", ip4_addr1_16(&test_packet->etharp_hdr.sipaddr.addrw), ip4_addr2_16(&test_packet->etharp_hdr.sipaddr.addrw), ip4_addr3_16(&test_packet->etharp_hdr.sipaddr.addrw), ip4_addr4_16(&test_packet->etharp_hdr.sipaddr.addrw)); printf("+-------------------+\n"); printf("| %02X:%02X:%02X:%02X:%02X:%02X | (dest MAC)\n", test_packet->etharp_hdr.dhwaddr.addr[0], test_packet->etharp_hdr.dhwaddr.addr[1], test_packet->etharp_hdr.dhwaddr.addr[2], test_packet->etharp_hdr.dhwaddr.addr[3], test_packet->etharp_hdr.dhwaddr.addr[4], test_packet->etharp_hdr.dhwaddr.addr[5]); printf("+-------------------+\n"); printf("| %3hu.%3hu.%3hu.%3hu | (dest IP)\n", ip4_addr1_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr2_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr3_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr4_16(&test_packet->etharp_hdr.dipaddr.addrw)); printf("+-------------------+\n"); } }
구조체를 초기화 시켜줍니다.
안하면 데이터가 요상하게 나올수도 있습니다~
memset(test_packet, 0, sizeof((ETH_TEST_PACKET *)test_packet)); memset(buff, 0, sizeof(buff) );
출력 방법은 간단 합니다.
소스에서 보이듯이 그냥 받아온 변수 값을 하나씩 이쁘게 출력해주면 됩니다.
(이쁘게 보이기 위한 노가다의 산물입니다. orz)
printf("| %04X | ", htons(test_packet->eth_hdr.type));
중간에 이런게 보이실겁니다.
저번에 설명 드렸다시피 엔디안의 차이로 0608으로 날려주고
받는 쪽에서 htons라는 함수를 사용해서 0806으로 다시 변경 시켜 주는 작업입니다.
printf("| %3hu.%3hu.%3hu.%3hu | (dest IP)\n", ip4_addr1_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr2_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr3_16(&test_packet->etharp_hdr.dipaddr.addrw), ip4_addr4_16(&test_packet->etharp_hdr.dipaddr.addrw));
ip4_addr1_x 라고 적혀 있는 부분은 아래와 같이 되어 있습니다.
/* Get one byte from the 4-byte address */ #define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0]) #define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1]) #define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2]) #define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3]) #define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) #define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) #define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) #define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
이 부분은 받아온 데이터를 ipv4 형식에 맞도록 u8 형식 4개로 바꿔줍니다.
요기서 수신까지 끝마치겠습니다~
짧은 글 읽어 주셔서 감사합니다!
이미지 출처 : 위키피디아