440 lines
14 KiB
C
440 lines
14 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdhcps_packet.c,v $ $Revision: 1.24.10.4 $
|
|
* $Source: /home/interpeak/CVSRoot/ipdhcps/src/ipdhcps_packet.c,v $
|
|
* $Author: jren $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
*
|
|
* Design and implementation by Jonas Rendel <jonas@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipdhcps_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_shell.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_tmo2.h>
|
|
#include <ipcom_type.h>
|
|
|
|
#include "ipdhcps.h"
|
|
#include "ipdhcps_h.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IP_ETH_ADDR_SIZE
|
|
#define IP_ETH_ADDR_SIZE 6
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_EXTERN Ipdhcps_data ipdhcpsc;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC Ip_ssize_t ipdhcps_send_to_relay_agent(Ipdhcps_handle *handle);
|
|
IP_STATIC Ip_ssize_t ipdhcps_send_to_client (Ipdhcps_handle *handle,
|
|
Ip_bool force_broadcast);
|
|
IP_STATIC Ip_ssize_t ipdhcps_send_execute (Ipdhcps_handle *handle,
|
|
Ip_u32 dst, Ip_u16 port);
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC Ip_u8 chaddr_bcast[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps_send_to_client
|
|
*===========================================================================
|
|
* Description: DHCP send routine used when transmitting to clients that
|
|
* can be reached directly (no relay agent inbetween).
|
|
* Because we can not assume that the client will respond to
|
|
* arp requests we can not use ordinary udp sockets. Instead
|
|
* we build raw DHCP/UDP/IP packets ourselves and transmit them
|
|
* on a packet socket using the clients link-layer address.but when
|
|
* it comes to INFORM MESSAGE, udp socket is a better choice.
|
|
*
|
|
* Parameters: handle - pointer to ipdhcps handle
|
|
* Returns: number of bytes sent. -1 if something failed.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_ssize_t
|
|
ipdhcps_send_to_client(Ipdhcps_handle *handle, Ip_bool force_broadcast)
|
|
{
|
|
Ip_ssize_t retval;
|
|
Ip_u32 ciaddr;
|
|
Ip_u8 *chaddr;
|
|
int fd;
|
|
struct Ip_sockaddr_ll dest;
|
|
|
|
#define IPDHCPS_MSG_INFORM 8
|
|
|
|
if ( handle->saved_msg_type == IPDHCPS_MSG_INFORM&&handle->packet->hdr.ciaddr)
|
|
return ipdhcps_send_execute (handle, handle->packet->hdr.ciaddr, ipdhcpsc.config.client_port);
|
|
|
|
/* Get packet socket to write raw frames on */
|
|
if((fd = ipcom_socket(IP_AF_PACKET,
|
|
IP_SOCK_DGRAM,
|
|
ip_htons(IP_ETH_P_IP))) == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG0(DEBUG, "ipdhcps_send :: failed to get packet socket.");
|
|
IPDHCPS_LEAVE(-1);
|
|
}
|
|
|
|
/* Find out destination address */
|
|
if((force_broadcast == IP_TRUE)
|
|
|| (ip_ntohs(handle->packet->hdr.flags) & IPDHCPS_BROADCAST_FLAG))
|
|
{
|
|
/* Client must be broadcasted to */
|
|
ciaddr = IP_INADDR_BROADCAST;
|
|
chaddr = chaddr_bcast;
|
|
}
|
|
else if(handle->packet->hdr.ciaddr != 0)
|
|
{
|
|
/* Unicast packet to clients ciaddr */
|
|
ciaddr = handle->packet->hdr.ciaddr;
|
|
chaddr = handle->packet->hdr.chaddr;
|
|
}
|
|
else
|
|
{
|
|
/* Unicast packet to clients yiaddr */
|
|
ciaddr = handle->packet->hdr.yiaddr;
|
|
chaddr = handle->packet->hdr.chaddr;
|
|
}
|
|
|
|
/* Setup destination ll address */
|
|
ipcom_memset(&dest, 0, sizeof(dest));
|
|
|
|
dest.sll_family = IP_AF_PACKET;
|
|
dest.sll_protocol = ip_htons(IP_ETH_P_IP);
|
|
dest.sll_ifindex = (Ip_u32)handle->recv_if->ifindex;
|
|
dest.sll_halen = IP_ETH_ADDR_SIZE;
|
|
|
|
ipcom_memcpy(dest.sll_addr, chaddr, IP_ETH_ADDR_SIZE);
|
|
|
|
/* Clear IP and UDP headers */
|
|
ipcom_memset(handle->raw_packet,
|
|
0,
|
|
sizeof(Ipcom_ip_header) +
|
|
sizeof(Ipdhcps_udp_header));
|
|
|
|
/* Init headers */
|
|
handle->raw_packet->ip.protocol = IP_IPPROTO_UDP;
|
|
handle->raw_packet->ip.src.addr32 = handle->recv_if->siaddr;
|
|
handle->raw_packet->ip.dst.addr32 = ciaddr;
|
|
handle->raw_packet->udp.src_port = ip_htons(ipdhcpsc.config.server_port);
|
|
handle->raw_packet->udp.dst_port = ip_htons(ipdhcpsc.config.client_port);
|
|
handle->raw_packet->udp.length = (Ip_u16)(ip_htons(sizeof(Ipdhcps_udp_header) + handle->packet_len));
|
|
handle->raw_packet->ip.length = handle->raw_packet->udp.length;
|
|
|
|
/* Calculate UDP checksum (pseudo header+header+payload) */
|
|
handle->raw_packet->udp.checksum = ipcom_in_checksum((Ip_u16*)handle->raw_packet,
|
|
sizeof(Ipcom_ip_header) +
|
|
sizeof(Ipdhcps_udp_header) +
|
|
handle->packet_len );
|
|
|
|
handle->raw_packet->ip.length = (Ip_u16)(ip_htons(sizeof(Ipcom_ip_header) +
|
|
sizeof(Ipdhcps_udp_header) +
|
|
handle->packet_len));
|
|
|
|
handle->raw_packet->ip.vers_len = 0x40; /* Ipv4 */
|
|
handle->raw_packet->ip.vers_len |= sizeof(Ipcom_ip_header) / sizeof(Ip_u32);
|
|
|
|
handle->raw_packet->ip.ttl = 64;
|
|
handle->raw_packet->ip.checksum = ipcom_in_checksum((Ip_u16*)&handle->raw_packet->ip,
|
|
sizeof(Ipcom_ip_header));
|
|
|
|
/* Transmit packet */
|
|
retval = ipcom_sendto(fd,
|
|
handle->raw_packet,
|
|
(Ip_size_t) ip_ntohs(handle->raw_packet->ip.length),
|
|
0,
|
|
(struct Ip_sockaddr*) &dest,
|
|
sizeof(struct Ip_sockaddr_ll));
|
|
leave:
|
|
|
|
if(fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(fd);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps_send_execute
|
|
*===========================================================================
|
|
* Description: DHCP send routine used when transmitting to clients that
|
|
* can be reached via a relay agent or the ACK is for INFORM.
|
|
* We use a standard udp socket to send the dhcp packet
|
|
*
|
|
* Parameters: handle - pointer to ipdhcps handle
|
|
* dst - destination
|
|
* port - remote port
|
|
* Returns: number of bytes sent. -1 if something failed.
|
|
*
|
|
*/
|
|
|
|
|
|
IP_STATIC Ip_ssize_t
|
|
ipdhcps_send_execute (Ipdhcps_handle *handle, Ip_u32 dst, Ip_u16 port)
|
|
{
|
|
Ip_ssize_t retval;
|
|
int opt;
|
|
int fd;
|
|
struct Ip_sockaddr_in addr;
|
|
|
|
if((fd = ipcom_socket(IP_AF_INET,
|
|
IP_SOCK_DGRAM,
|
|
IP_IPPROTO_UDP)) == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG0(DEBUG,"ipdhcps_send :: failed to get socket.");
|
|
IPDHCPS_LEAVE(-1);
|
|
}
|
|
|
|
opt = 1;
|
|
if(ipcom_setsockopt(fd,
|
|
IP_SOL_SOCKET,
|
|
IP_SO_REUSEADDR,
|
|
&opt,
|
|
sizeof(opt)) == -1)
|
|
{
|
|
IPCOM_LOG0(DEBUG, "ipdhcps_send :: failed to set sockopt.");
|
|
IPDHCPS_LEAVE(-1);
|
|
}
|
|
|
|
/* Bind socket to receiving interface */
|
|
memset(&addr, 0, sizeof(struct Ip_sockaddr_in));
|
|
addr.sin_family = IP_AF_INET;
|
|
addr.sin_port = ip_htons(ipdhcpsc.config.server_port);
|
|
addr.sin_addr.s_addr = handle->recv_if->siaddr;
|
|
|
|
if(ipcom_bind(fd,
|
|
(struct Ip_sockaddr*)&addr,
|
|
sizeof(struct Ip_sockaddr_in)) == -1)
|
|
{
|
|
IPCOM_LOG0(DEBUG, "ipdhcps_send :: failed to bind socket.");
|
|
IPDHCPS_LEAVE(-1);
|
|
}
|
|
|
|
/* Send to agent */
|
|
addr.sin_port = ip_htons(port);
|
|
addr.sin_addr.s_addr = dst;
|
|
|
|
retval = ipcom_sendto(fd,
|
|
handle->packet,
|
|
handle->packet_len,
|
|
0,
|
|
(struct Ip_sockaddr*)&addr,
|
|
sizeof(struct Ip_sockaddr_in));
|
|
leave:
|
|
|
|
if(fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(fd);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps_send_to_relay_agent
|
|
*===========================================================================
|
|
* Description: DHCP send routine used when transmitting to clients that
|
|
* can only be reached via a relay agent.
|
|
* We use a standard udp socket to send the dhcp packet to
|
|
* the relay agent in question.
|
|
*
|
|
* Parameters: handle - pointer to ipdhcps handle
|
|
* Returns: number of bytes sent. -1 if something failed.
|
|
*
|
|
*/
|
|
|
|
IP_STATIC Ip_ssize_t
|
|
ipdhcps_send_to_relay_agent(Ipdhcps_handle *handle)
|
|
{
|
|
ip_assert(handle->packet != IP_NULL);
|
|
ip_assert(handle->packet->hdr.giaddr != 0);
|
|
return ipdhcps_send_execute (handle, handle->packet->hdr.giaddr, ipdhcpsc.config.server_port);
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps_send_packet
|
|
*===========================================================================
|
|
* Description: Wrapper function for sending dhcp packets.
|
|
*
|
|
* Parameters: handle - pointer to ipdhcps handle
|
|
* Returns: number of bytes sent.
|
|
* -1 if something failed.
|
|
*/
|
|
|
|
IP_GLOBAL Ip_ssize_t
|
|
ipdhcps_send_packet(Ipdhcps_handle *handle, Ip_bool force_broadcast)
|
|
{
|
|
Ip_ssize_t retval;
|
|
|
|
if(handle->packet->hdr.giaddr != 0)
|
|
retval = ipdhcps_send_to_relay_agent(handle);
|
|
else
|
|
retval = ipdhcps_send_to_client(handle, force_broadcast);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdhcps_receive_packet
|
|
*===========================================================================
|
|
* Description: This function is used for receiving packets of type DHCP.
|
|
* only packets of type DHCP received on interfaces define and
|
|
* enabled for IPDHCPS are forwarded to the caller.
|
|
*
|
|
* Parameters: fd - receive socket descriptor
|
|
* handle - ipdhcps handle
|
|
*
|
|
* Returns: number of bytes received.
|
|
* -1 if something failed.
|
|
*/
|
|
|
|
IP_GLOBAL Ip_ssize_t
|
|
ipdhcps_receive_packet(int fd, Ipdhcps_handle *handle)
|
|
{
|
|
struct Ip_msghdr msg;
|
|
struct Ip_iovec iov[1];
|
|
struct Ip_cmsghdr *cmptr;
|
|
struct Ip_in_pktinfo *pktinfo;
|
|
struct Ip_sockaddr_in src_addr;
|
|
Ip_ssize_t n;
|
|
union {
|
|
struct Ip_cmsghdr cm;
|
|
char control[IP_CMSG_SPACE(sizeof(struct Ip_in_pktinfo))];
|
|
} control_un;
|
|
|
|
iov[0].iov_base = handle->packet;
|
|
iov[0].iov_len =(Ip_size_t)ipdhcpsc.config.packet_size;
|
|
|
|
msg.msg_name = &src_addr;
|
|
msg.msg_namelen = sizeof(src_addr);
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = control_un.control;
|
|
msg.msg_controllen = sizeof(control_un.control);
|
|
msg.msg_flags = 0;
|
|
|
|
ipcom_memset(&src_addr, 0, sizeof(src_addr));
|
|
ipcom_memset(iov[0].iov_base, 0, iov[0].iov_len);
|
|
n = ipcom_recvmsg(fd, &msg, 0);
|
|
|
|
if(n > 0)
|
|
{
|
|
/* Get info about receiving interface */
|
|
if(((cmptr = IP_CMSG_FIRSTHDR(&msg)) != IP_NULL) &&
|
|
cmptr->cmsg_len == IP_CMSG_LEN(sizeof(struct Ip_in_pktinfo)))
|
|
{
|
|
pktinfo = (struct Ip_in_pktinfo*)IP_CMSG_DATA(cmptr);
|
|
handle->recv_if = ipdhcps_interface_find((int)pktinfo->ipi_ifindex);
|
|
handle->dst_addr = pktinfo->ipi_addr.s_addr;
|
|
handle->src_addr = src_addr.sin_addr.s_addr;
|
|
|
|
/* Drop if interface unknown */
|
|
if(handle->recv_if == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(INFO,
|
|
"ipdhcps_daemon :: unable to locate interface %d",
|
|
pktinfo->ipi_ifindex);
|
|
return 0;
|
|
}
|
|
|
|
/* Drop if interface disabled */
|
|
if(handle->recv_if->enabled == IP_FALSE
|
|
|| handle->recv_if->functional == IP_FALSE)
|
|
return 0;
|
|
|
|
/* The vend data could be NULL in BOOTP */
|
|
if (ip_ntohl(handle->packet->hdr.cookie) == 0)
|
|
return n;
|
|
/* Drop if packet is not DHCP */
|
|
else if(ip_ntohl(handle->packet->hdr.cookie) != IPDHCPS_MAGIC_COOKIE)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG0(DEBUG, "ipdhcps_receive :: got no interface info");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|