/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipdhcps_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #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 **************************************************************************** */