저는 IPv6 용으로 자체적으로 단순화 된 TCP/IP 스택을 구현하려고 시도하고 있으며, 현재 내 목표는 ICMPv6 에코 요청에 응답 할 수 있도록하는 것입니다.C에서 ICMPv6 체크섬 계산이 잘못된 결과를 반환합니다.
typedef uint16_t n_uint16_t; //network byte order
typedef uint32_t n_uint32_t;
n_uint16_t htons(uint16_t n);
n_uint32_t htonl(uint32_t n);
struct ipv6hdr {
n_uint32_t vtcfl; //version, traffic class, flow label
n_uint16_t payload_len;
unsigned char nexthdr;
unsigned char hop_limit;
unsigned char saddr[IP6_ALEN]; //IP6_ALEN = 16
unsigned char daddr[IP6_ALEN];
};
struct icmp6hdr {
unsigned char type;
unsigned char code;
n_uint16_t cksum;
union {
n_uint32_t un_data32[1]; /* type-specific field */
n_uint16_t un_data16[2]; /* type-specific field */
unsigned char un_data8[4]; /* type-specific field */
} dataun;
};
(endianity를 처리도 정의 타입)
I에서는 ICMPv6을 체크섬을 계산하기위한 함수는 다음의 사용
는 나중에 계산에 필요한 데이터를 저장하기위한 구조를 다음을 사용. 처음 두 개는 그에 따라 ICMPv6 패킷 및 IPv6 의사 헤더 필드에서 버퍼를 생성합니다. 그런 다음 버퍼의 16 비트 필드 합계의 1의 보수를 계산합니다.
n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp) {
unsigned char buf[65535];
unsigned char *ptr = &(buf[0]);
int chksumlen = 0;
//ICMPv6 type
memcpy(ptr, &icmp->type, sizeof(icmp->type));
ptr += sizeof(icmp->type);
chksumlen += sizeof(icmp->type);
//ICMPv6 code
memcpy(ptr, &icmp->code, sizeof(icmp->code));
ptr += sizeof(icmp->code);
chksumlen += sizeof(icmp->code);
//ICMPv6 payload
memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32));
ptr += sizeof(icmp->dataun.un_data32);
chksumlen += sizeof(icmp->dataun);
return pseudoheaderchksum(buf, ptr, ip6, chksumlen);
}
n_uint16_t pseudoheaderchksum(unsigned char *buf, unsigned char *ptr, struct ipv6hdr *ip6, int chksumlen) {
//source address
memcpy(ptr, &ip6->saddr, sizeof(ip6->saddr));
ptr += sizeof(ip6->saddr);
chksumlen += sizeof(ip6->saddr);
//dest address
memcpy(ptr, &ip6->daddr, sizeof(ip6->daddr));
ptr += sizeof(ip6->daddr);
chksumlen += sizeof(ip6->daddr);
//upper layer length
n_uint32_t upprlen = 0;
upprlen += sizeof(ip6->payload_len);
memcpy(ptr, &upprlen, sizeof(upprlen));
ptr += 4;
chksumlen += 4;
//3 bytes of zeros, then next header byte
*ptr = 0;
ptr++;
*ptr = 0;
ptr++;
*ptr = 0;
ptr++;
chksumlen += 3;
memcpy(ptr, &ip6->nexthdr, sizeof(ip6->nexthdr));
ptr += sizeof(ip6->nexthdr);
chksumlen += sizeof(ip6->nexthdr);
return chksum((uint16_t *) buf, chksumlen);
}
//counting internet checksum
n_uint16_t chksum(uint16_t *buf, int len) {
int count = len;
n_uint32_t sum = 0;
n_uint16_t res = 0;
while (count > 1) {
sum += (*(buf));
buf++;
count -= 2;
}
//if number of bytes was odd
if (count > 0) {
sum += *(unsigned char *) buf;
}
while (sum >> 16) {
sum = sum + (sum >> 16);
}
res = (n_uint16_t) sum;
return ~res;
}
불행히도 기존 패킷에서 테스트 할 때 캡처 된 ICMPv6의 체크섬이 계산 결과와 다릅니다. 내가 뭘 잘못 했니?
추신. 원시 이더넷 패킷을 캡처 및/또는 보내려면 libpcap을 사용하고 있습니다.
편집 : 기능
icmpv6_chksum 수행하는 작업에 대한 자세한 설명은 - 된 ICMPv6 패킷 플러스는 캡슐화 된 것있는 IPv6 패킷의 헤더의 구조를 가져옵니다. 나중에 계산할 빈 버퍼를 만들고 ICMPv6 유형, ICMPv6 코드, ICMPv6 메시지 (기본적으로 ICMPv6 패킷 전체, 체크섬 필드 제외, 계산 중에는 0이됩니다)의 값을 복사합니다.
버퍼, 첫 번째 빈 위치 포인터 및 IPv6 헤더를 pseudoheaderchecksum에 전달합니다.
pseudoheaderchecksum - 버퍼, 첫 번째 빈 위치 포인터 및 IPv6 헤더를 가져옵니다. IPv6 소스 주소, IPv6 대상 주소, 데이터 길이 (이 경우 ICMPv6 패킷 길이), 3 제로 바이트 및 IPv6 다음 헤더 값 (= ICMPv6 헤더)을 복사합니다.
이 기능은 소위 "IPv6 가상 헤더"라고 불리는 버퍼에 추가됩니다. RFC 2460.
이제 채워진 버퍼가 chksum 함수로 전달됩니다.이 함수는 버퍼에 대한 인터넷 체크섬 수를 계산합니다.
chksum - 버퍼와 그 길이를 바이트 단위로 가져옵니다. 버퍼의 16 비트 단편을 합계합니다 (필요에 따라 마지막으로 홀수 바이트를 추가합니다). 오버플로가 16 비트 합계에 추가됩니다. 마지막으로 함수는 계산 결과의 1의 보수 (2 진 반전)를 반환합니다.
RFC 1071에 설명 된대로이 함수에서 인터넷 체크섬을 계산하려고합니다. 모범 구현을 살펴보면 (비록 내가 100 % 확신 할 수는 없지만 알고리즘 설명이 종종 모호합니다) 올바른 것이되어야합니다.
2
좋아 편집, 나는 이제 된 ICMPv6 메시지 본문 (헤더를 따르고 있습니다 내용을) 포함되지 않은 것으로 나타났습니다 unsigned char *data
온 모든 것이
n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp, unsigned char* data, int len) {
unsigned char buf[65535];
unsigned char *ptr = &(buf[0]);
int chksumlen = 0;
//ICMPv6 type
memcpy(ptr, &icmp->type, sizeof(icmp->type));
ptr += sizeof(icmp->type);
chksumlen += sizeof(icmp->type);
//ICMPv6 code
memcpy(ptr, &icmp->code, sizeof(icmp->code));
ptr += sizeof(icmp->code);
chksumlen += sizeof(icmp->code);
//ICMPv6 rest of header
memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32));
ptr += sizeof(icmp->dataun.un_data32);
chksumlen += sizeof(icmp->dataun);
unsigned char *tmp = data;
for(int i=0; i<len; i++){
memcpy(ptr, &tmp, sizeof(unsigned char));
ptr += sizeof(unsigned char);
tmp += sizeof(unsigned char);
chksumlen += sizeof(unsigned char);
}
return pseudoheader_chksum(buf, ptr, ip6, chksumlen);
}
ICMPv6 헤더 이후 (이더넷 FCS 제외).
체크섬 계산이 여전히 제대로 작동하지 않습니다.
이 체크섬 알고리즘에 대한 정확하고 완전한 설명을 추가하십시오! 나는'htonl()'과 친구들 호출이 보이지 않는다. 왜? –
이러한 구조체에는 '압축 된'pragma가 필요할 수 있습니다. –
체크섬에 IPv6 패킷 헤더를 포함하지 마십시오. 의사 헤더를 ICMP 데이터 그램 앞에 추가 한 다음 의사 헤더와 데이터 그램을 통해 체크섬을 계산하십시오. –