#include "json-util.h"
#include "network-common.h"
#include "set.h"
+#include "socket-util.h"
#include "sort-util.h"
#include "string-util.h"
#include "unaligned.h"
*ret = TAKE_PTR(message);
return 0;
}
+
+int dhcp_message_send_udp(
+ sd_dhcp_message *message,
+ int fd,
+ be32_t src_addr,
+ be32_t dst_addr,
+ uint16_t dst_port) {
+
+ int r;
+
+ assert(message);
+ assert(fd >= 0);
+
+ _cleanup_(iovw_done_free) struct iovec_wrapper payload = {};
+ r = dhcp_message_build(message, &payload);
+ if (r < 0)
+ return r;
+
+ union sockaddr_union sa = {
+ .in.sin_family = AF_INET,
+ .in.sin_port = htobe16(dst_port),
+ .in.sin_addr.s_addr = dst_addr,
+ };
+
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
+
+ struct msghdr msg = {
+ .msg_name = &sa,
+ .msg_namelen = sizeof(sa.in),
+ .msg_iov = payload.iovec,
+ .msg_iovlen = payload.count,
+ };
+
+ if (src_addr != INADDR_ANY) {
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ struct cmsghdr *cmsg = ASSERT_PTR(CMSG_FIRSTHDR(&msg));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+
+ struct in_pktinfo *pktinfo = ASSERT_PTR(CMSG_TYPED_DATA(cmsg, struct in_pktinfo));
+ pktinfo->ipi_spec_dst.s_addr = src_addr;
+ }
+
+ if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int dhcp_message_send_raw(
+ sd_dhcp_message *message,
+ int fd,
+ int ifindex,
+ be32_t src_addr,
+ uint16_t src_port,
+ const struct hw_addr_data *dst_hw_addr,
+ be32_t dst_addr,
+ uint16_t dst_port,
+ int ip_service_type) {
+
+ int r;
+
+ assert(message);
+ assert(fd >= 0);
+ assert(ifindex > 0);
+ assert(dst_hw_addr);
+
+ _cleanup_(iovw_done_free) struct iovec_wrapper payload = {};
+ r = dhcp_message_build(message, &payload);
+ if (r < 0)
+ return r;
+
+ struct iphdr ip;
+ struct udphdr udp;
+ r = udp_packet_build(
+ src_addr,
+ src_port,
+ dst_addr,
+ dst_port,
+ ip_service_type,
+ &payload,
+ &ip,
+ &udp);
+ if (r < 0)
+ return r;
+
+ _cleanup_(iovw_done) struct iovec_wrapper iovw = {};
+ r = iovw_put(&iovw, &ip, sizeof(struct iphdr));
+ if (r < 0)
+ return r;
+
+ r = iovw_put(&iovw, &udp, sizeof(struct udphdr));
+ if (r < 0)
+ return r;
+
+ r = iovw_put_iovw(&iovw, &payload);
+ if (r < 0)
+ return r;
+
+ union sockaddr_union sa = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htobe16(ETH_P_IP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = dst_hw_addr->length,
+ };
+
+ memcpy_safe(sa.ll.sll_addr, dst_hw_addr->bytes, dst_hw_addr->length);
+
+ struct msghdr mh = {
+ .msg_name = &sa.sa,
+ .msg_namelen = sockaddr_ll_len(&sa.ll),
+ .msg_iov = iovw.iovec,
+ .msg_iovlen = iovw.count,
+ };
+
+ if (sendmsg(fd, &mh, MSG_NOSIGNAL) < 0)
+ return -errno;
+
+ return 0;
+}
#include "dns-packet.h"
#include "dns-resolver-internal.h"
#include "ether-addr-util.h"
+#include "fd-util.h"
#include "iovec-util.h"
#include "iovec-wrapper.h"
+#include "ip-util.h"
#include "random-util.h"
#include "set.h"
+#include "socket-util.h"
#include "strv.h"
#include "tests.h"
ASSERT_TRUE(iovw_equal(&iovw, expected));
}
+static void verify_send_udp(sd_dhcp_message *message, uint32_t xid, const struct hw_addr_data *hw_addr) {
+ _cleanup_close_pair_ int socket_fd[2] = EBADF_PAIR;
+ ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socket_fd));
+
+ ASSERT_OK(dhcp_message_send_udp(
+ message,
+ socket_fd[0],
+ /* src_addr= */ htobe32(0xC0000201), /* 192.0.2.1 */
+ /* dst_addr= */ htobe32(0xC0000202), /* 192.0.2.2 */
+ /* dst_port= */ DHCP_PORT_CLIENT));
+
+ ssize_t buflen = ASSERT_OK_POSITIVE(next_datagram_size_fd(socket_fd[1]));
+ _cleanup_free_ void *buf = ASSERT_NOT_NULL(malloc0(buflen));
+
+ struct msghdr msg = {
+ .msg_iov = &IOVEC_MAKE(buf, buflen),
+ .msg_iovlen = 1,
+ };
+ ssize_t len = ASSERT_OK_ERRNO(recvmsg_safe(socket_fd[1], &msg, MSG_DONTWAIT));
+
+ _cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
+ ASSERT_OK(dhcp_message_parse(
+ &IOVEC_MAKE(buf, len),
+ BOOTREQUEST,
+ &xid,
+ ARPHRD_ETHER,
+ hw_addr,
+ &m));
+
+ /* Verify the received message. */
+ _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}, iovw2 = {};
+ ASSERT_OK(dhcp_message_build(message, &iovw));
+ ASSERT_OK(dhcp_message_build(m, &iovw2));
+ ASSERT_TRUE(iovw_equal(&iovw, &iovw2));
+}
+
+static void verify_send_raw(sd_dhcp_message *message, uint32_t xid, const struct hw_addr_data *hw_addr) {
+ _cleanup_close_pair_ int socket_fd[2] = EBADF_PAIR;
+ ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socket_fd));
+
+ ASSERT_OK(dhcp_message_send_raw(
+ message,
+ socket_fd[0],
+ /* ifindex= */ 42,
+ /* src_addr= */ htobe32(0xC0000201), /* 192.0.2.1 */
+ /* src_port= */ DHCP_PORT_SERVER,
+ /* dst_hw_addr= */ &(struct hw_addr_data) {
+ .length = 6,
+ .ether = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }},
+ },
+ /* dst_addr= */ htobe32(0xC0000202), /* 192.0.2.2 */
+ /* dst_port= */ DHCP_PORT_CLIENT,
+ /* ip_service_type= */ IPTOS_CLASS_CS6));
+
+ ssize_t buflen = ASSERT_OK_POSITIVE(next_datagram_size_fd(socket_fd[1]));
+ _cleanup_free_ void *buf = ASSERT_NOT_NULL(malloc0(buflen));
+
+ struct msghdr msg = {
+ .msg_iov = &IOVEC_MAKE(buf, buflen),
+ .msg_iovlen = 1,
+ };
+ ssize_t len = ASSERT_OK_ERRNO(recvmsg_safe(socket_fd[1], &msg, MSG_DONTWAIT));
+
+ struct iovec payload;
+ ASSERT_OK(udp_packet_verify(
+ &IOVEC_MAKE(buf, len),
+ DHCP_PORT_CLIENT,
+ /* checksum= */ true,
+ &payload));
+
+ _cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
+ ASSERT_OK(dhcp_message_parse(
+ &payload,
+ BOOTREQUEST,
+ &xid,
+ ARPHRD_ETHER,
+ hw_addr,
+ &m));
+
+ /* Verify the received message. */
+ _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}, iovw2 = {};
+ ASSERT_OK(dhcp_message_build(message, &iovw));
+ ASSERT_OK(dhcp_message_build(m, &iovw2));
+ ASSERT_TRUE(iovw_equal(&iovw, &iovw2));
+}
+
TEST(dhcp_message) {
_cleanup_(sd_dhcp_message_unrefp) sd_dhcp_message *m = NULL;
_cleanup_(iovw_done_free) struct iovec_wrapper iovw3 = {};
ASSERT_OK(dhcp_message_build(m3, &iovw3));
ASSERT_TRUE(iovw_equal(&iovw, &iovw3));
+
+ /* send */
+ verify_send_udp(m, xid, &hw_addr);
+ verify_send_raw(m, xid, &hw_addr);
}
static void test_domains_one(size_t len, const uint8_t *data, char * const *expected) {