我正在编写简单的DHCP客户端,因此,根据RFC 951,我需要发送IP源地址=" 0.0.0.0"的数据包。我知道可以使用RAW套接字,但显然我的代码中有些东西是错误的 - 源地址总是由内核填充(到其他接口的定义地址)。
if (sockfd==0)
{
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (sockfd<0)
{
return result.setError(-1, "Can't create socket.");
}
status = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, m_ifName, strlen(m_ifName));
if (status<0)
{
return result.setError(status, "Can't bind socket to the interface.");
}
int optVal = 1;
status = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optVal, sizeof(optVal));
if (status != 0)
{
return result.setError(status, "Can't set IP_HDRINCL option on a socket");
}
int broadcastVal = 1;
status = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const void*>(&broadcastVal), sizeof(broadcastVal));
if (status!=0)
{
return result.setError(status, "Can't set a broadcast option on a socket.");
}
int reuseAddrVal = 1;
status = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseAddrVal, sizeof(reuseAddrVal));
if(status != 0)
return result.setError(status, "Can't set a SO_REUSEADDR opt on socket.");
struct sockaddr_in src_addr;
src_addr.sin_family = AF_INET;
src_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
src_addr.sin_port = htons(sourcePort);
status = bind(sockfd, reinterpret_cast<sockaddr *>(&src_addr), sizeof(sockaddr_in));
if (status != 0)
{
return result.setError(status, "Can't bind to the socket.");
}
}
unsigned char buffer[8192];
struct sockaddr_in dest_addr;
char* packetData = (char*)(buffer + sizeof(struct ip) + sizeof(struct udphdr));
struct ip* ip_header = (struct ip*) buffer;
struct udphdr* udp_header = (struct udphdr*) (buffer + sizeof(struct ip));
memset(buffer, 0, sizeof(buffer));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = INADDR_BROADCAST;
dest_addr.sin_port = htons(67);
ip_header->ip_v = 4;
ip_header->ip_hl = 5;
ip_header->ip_tos = 0;
ip_header->ip_id = 0;
ip_header->ip_ttl = 63;
ip_header->ip_p = IPPROTO_UDP;
ip_header->ip_off = 0;
ip_header->ip_sum = 0;
ip_header->ip_src.s_addr = inet_addr("0.0.0.0");
ip_header->ip_dst.s_addr = htonl(INADDR_BROADCAST);
udp_header->source = htons(68);
udp_header->dest = htons(67);
udp_header->check = htons(0);
DhcpData data(sizeof(DhcpHdr), 0);
prepareDhcpPacket(type, data, result);
if (result.errCode != 0)
{
return result;
}
strcpy(packetData, (const char*) data.constData());
ip_header->ip_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + data.size());
udp_header->len = htons(sizeof(struct udphdr) + data.size());
ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);
int segment_len = (sizeof(struct iphdr) + sizeof(struct udphdr) + data.size()) - ip_header->ip_hl*4;
int header_len = sizeof(PseudoHeader) + segment_len;
unsigned char* hdr = (unsigned char *)malloc(header_len);
PseudoHeader* pseudo_header;
pseudo_header = (PseudoHeader *)hdr;
pseudo_header->source_ip = ip_header->ip_src.s_addr;
pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
pseudo_header->reserved = 0;
pseudo_header->protocol = ip_header->ip_p;
pseudo_header->udp_length = htons(segment_len);
memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);
memcpy((hdr + sizeof(PseudoHeader) + 8), packetData, data.size());
udp_header->check = ComputeChecksum(hdr, header_len);
free(hdr);
int res = sendto(sockfd, buffer, (sizeof(struct ip) + sizeof(struct udphdr) + data.size()), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if(res < 0)
{
printf("Errno=%s", strerror(errno));
}
/* taken from TCP/IP Illustrated Vol. 2(1995) by Gary R. Wright and W. Richard Stevens. Page 236 */
unsigned short ComputeChecksum(unsigned char *data, int len)
{
long sum = 0; /* assume 32 bit long, 16 bit short */
unsigned short *temp = (unsigned short *)data;
while(len > 1){
sum += *temp++;
if(sum & 0x80000000) /* if high order bit set, fold */
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if(len) /* take care of left over byte */
sum += (unsigned short) *((unsigned char *)temp);
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ~sum;
}
答案 0 :(得分:1)
Reptile
不允许更改0.0.0.0上的IP,请参阅man:raw(7)
来源地址│零时填写
(0.0.0.0)是zerro
您必须使用:
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
是的,您必须再生成14个字节,现在是以太网标头
简单示例请参阅:simple DHCP client