/*
* net_functios.c
*
* Created on: 30.07.2009
* Copyright (C) 2009 by Chebotarev Roman <roma@ultranet.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common_includes.h"
#include "dhcp.h"
#include "dhcdrop_types.h"
#include "net_functions.h"
int is_timeout;
uint16_t rs_crc(const unsigned short *buffer, int length)
{
uint32_t crc = 0;
/* Calculate CRC */
while (length > 1)
{
crc += *buffer++;
length -= sizeof (unsigned short);
}
if (length)
crc += *(unsigned char*) buffer;
crc = (crc >> 16) + (crc & 0xFFFF);
crc += (crc >> 16);
return (uint16_t)(~crc);
}
void assemble_net_header_arp(struct arp_packet_net_header * net_header,
const uint8_t * ether_src,
uint16_t op_code)
{
/* Fill ethernet header */
memcpy(net_header->eth_head.ether_shost, ether_src, sizeof(net_header->eth_head.ether_shost));
memset(net_header->eth_head.ether_dhost, 0xFF, sizeof(net_header->eth_head.ether_dhost));
net_header->eth_head.ether_type = htons(ETHERTYPE_ARP);
/* Fill ARP header */
net_header->arp_head.arp_hwtype = htons(1); /* Ethernet */
net_header->arp_head.arp_proto = htons(ETHERTYPE_IP);
net_header->arp_head.arp_hwlen = ETH_ALEN;
net_header->arp_head.arp_palen = IPV4_ALEN;
net_header->arp_head.arp_oper = op_code;
}
void assemble_net_header_dhcp
(
struct dhcp_packet_net_header * header,
const int data_len,
const struct config_params * params,
const uint8_t * ether_src,
const uint32_t dst_ip,
const uint32_t src_ip
)
{
/* Fill ethernet header */
memcpy(header->eth_head.ether_shost, ether_src, sizeof(header->eth_head.ether_shost));
memset(header->eth_head.ether_dhost, 0xFF, sizeof(header->eth_head.ether_dhost));
header->eth_head.ether_type = htons(ETHERTYPE_IP);
/* Fill IP header */
bzero(&header->ip_header, sizeof(struct iphdr));
header->ip_header.ihl = IP_HDR_LEN;
header->ip_header.version = 4;
header->ip_header.tos = 0x10;
header->ip_header.tot_len = htons(B_IP_HDR_LEN + sizeof(struct udphdr) + data_len);
header->ip_header.id = (uint16_t) rand();
header->ip_header.frag_off = 0;
header->ip_header.ttl = DEF_TTL;
header->ip_header.protocol = IPPROTO_UDP;
header->ip_header.check = 0;
header->ip_header.saddr = src_ip;
if(dst_ip)
header->ip_header.daddr = dst_ip;
else
memset(&header->ip_header.daddr, 0xFF, sizeof(uint32_t)); /*Set dst IP = 255.255.255.255*/
header->ip_header.check = rs_crc((unsigned short*)&header->ip_header, sizeof(struct iphdr));
header->udp_header.source = htons(params->client_port);
header->udp_header.dest = htons(params->server_port);
header->udp_header.len = htons(sizeof(struct udphdr) + data_len);
header->udp_header.check = 0; /* Don't use because CRC for UDP - optional parametr */
return;
}
int send_packet(pcap_t* descr, enum packet_types packet_type, const uint8_t *snd_data, const int data_len,
const struct config_params * config,
const uint8_t * ether_src, const uint32_t dst_ip)
{
int header_size = 0;
static unsigned char pack_buf[DHCP_MTU_MAX];
bzero(pack_buf, sizeof(pack_buf));
/* Create net-header */
switch(packet_type)
{
case dhcp_packet:
if(!config)
return 0;
assemble_net_header_dhcp((struct dhcp_packet_net_header *)pack_buf,
data_len, config, ether_src, dst_ip, 0);//((struct dhcp_packet *) snd_data)->ciaddr.s_addr);
header_size = sizeof(struct dhcp_packet_net_header);
break;
case arp_packet:
assemble_net_header_arp((struct arp_packet_net_header *) pack_buf, ether_src, ARP_OP_REQ);
header_size = sizeof(struct arp_packet_net_header);
break;
default:
printf("send_packet(): unknown type of packet.\n");
return 0;
}
/* Copy user data to send buffer */
memcpy(pack_buf + header_size, snd_data, data_len);
if(pcap_inject(descr, pack_buf, header_size + data_len) == -1)
{
pcap_perror(descr, "pcap_inject");
return 0;
}
return 1;
}
void packet_handler(u_char *out_packet, const struct pcap_pkthdr *h,
const u_char *packet)
{
if(h->len > DHCP_MTU_MAX)
{
printf("Received too long packet: %d. Can't dispatch!\n", h->len);
return;
}
memcpy(out_packet, (u_char*)packet, h->len);
return;
}
int get_packet(pcap_t * descr, u_char * ether_packet, const int wait_seconds)
{
int ret = 0;
uint32_t t;
if(wait_seconds > 0)
{
is_timeout = 0;
t = timer_start(wait_seconds, timeout);
}
else
is_timeout = 1;
while(1)
{
if(wait_seconds)
usleep(CAP_TIMEOUT); /* Waiting 100 microseconds for avoid CPU overload */
ret = pcap_dispatch(descr, 1, packet_handler, (u_char*)ether_packet);
if(ret < 0)
{
perror("pcap_dispatch");
return -1;
}
if(is_timeout || ret)
{
timer_stop(t);
break;
}
}
return ret;
}
#ifdef _WIN32
void CALLBACK timeout(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
#else
void timeout(int signal)
#endif
{
is_timeout = 1;
}
/* Opening and testing device */
pcap_t * get_device(const struct config_params * params)
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t * pcap_socket = pcap_open_live(params->if_name, DHCP_MTU_MAX, 1,
PCAP_OPEN_LIVE_TIMEOUT, errbuf);
if(pcap_socket == NULL)
{
printf("Opening device error! %s\n",errbuf);
exit(ERR_OPENDEV);
}
if(pcap_datalink(pcap_socket) != DLT_EN10MB)
{
printf("Can't work on this link layer type! Exit.\n");
exit(ERR_INVALID_DEV);
}
#ifdef __linux__
/* Set nonblock mode for processing DHCP timeouts */
if(pcap_setnonblock(pcap_socket, 1, errbuf) == -1)
{
printf("pcap_setnoblock error: '%s'\n", errbuf);
pcap_close(pcap_socket);
exit(ERR_SETNOBLOCK);
}
#endif
return pcap_socket;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>