dhcp-message: introduce dhcp_message_send_{udp,raw}()
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#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"
|
||||
@@ -1645,3 +1646,126 @@ int dhcp_message_parse_json(sd_json_variant *v, sd_dhcp_message **ret) {
|
||||
*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;
|
||||
}
|
||||
|
||||
@@ -93,3 +93,20 @@ int dhcp_message_build(sd_dhcp_message *message, struct iovec_wrapper *ret);
|
||||
|
||||
int dhcp_message_build_json(sd_dhcp_message *message, sd_json_variant **ret);
|
||||
int dhcp_message_parse_json(sd_json_variant *v, sd_dhcp_message **ret);
|
||||
|
||||
int dhcp_message_send_udp(
|
||||
sd_dhcp_message *message,
|
||||
int fd,
|
||||
be32_t src_addr,
|
||||
be32_t dst_addr,
|
||||
uint16_t dst_port);
|
||||
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);
|
||||
|
||||
@@ -12,10 +12,13 @@
|
||||
#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"
|
||||
|
||||
@@ -189,6 +192,92 @@ static void verify_length_prefixed_data(sd_dhcp_message *m, const struct iovec_w
|
||||
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;
|
||||
|
||||
@@ -452,6 +541,10 @@ TEST(dhcp_message) {
|
||||
_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) {
|
||||
|
||||
Reference in New Issue
Block a user