Skip to content

DHCP Relay: add option -si to support using src intf ip in relay #7052

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
2 commits merged into from
Mar 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
From 16163f0693e30588216f0f280d5eba8bb53f7001 Mon Sep 17 00:00:00 2001
From: Tianrong Zhang <[email protected]>
Date: Fri, 12 Mar 2021 23:30:56 -0800
Subject: [PATCH] add option -si to support using src intf ip in relay

---
common/socket.c | 119 ++++++++++++++++++++++++++++++++++++-----------
includes/dhcpd.h | 1 +
relay/dhcrelay.c | 8 ++++
3 files changed, 100 insertions(+), 28 deletions(-)

diff --git a/common/socket.c b/common/socket.c
index 483eb9c..da9f501 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -83,6 +83,29 @@ static unsigned int global_v4_socket_references = 0;
static int global_v4_socket = -1;
#endif

+/*
+ * If set, uses "from" interface IP for packet Tx.
+ * If not set, kernel chooses appropriate src ip for tx pkts
+ */
+int use_src_intf_ip_for_tx;
+
+/*
+ * For both send_packet6() and receive_packet6() we need to allocate
+ * space for the cmsg header information. We do this once and reuse
+ * the buffer. We also need the control buf for send_packet() and
+ * receive_packet() when we use a single socket and IP_PKTINFO to
+ * send the packet out the correct interface.
+ */
+static void *v4_control_buf = NULL;
+static size_t v4_control_buf_len = 0;
+
+static void
+v4_allocate_cmsg_cbuf(void) {
+ v4_control_buf_len = CMSG_SPACE(sizeof(struct in_pktinfo));
+ v4_control_buf = dmalloc(v4_control_buf_len, MDL);
+ return;
+}
+
/*
* If we can't bind() to a specific interface, then we can only have
* a single socket. This variable insures that we don't try to listen
@@ -712,37 +735,77 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
struct hardware *hto;
{
int result;
-#ifdef IGNORE_HOSTUNREACH
- int retry = 0;
- do {
-#endif
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
- struct in_pktinfo pktinfo;
-
- if (interface->ifp != NULL) {
- memset(&pktinfo, 0, sizeof (pktinfo));
- pktinfo.ipi_ifindex = interface->ifp->ifr_index;
- if (setsockopt(interface->wfdesc, IPPROTO_IP,
- IP_PKTINFO, (char *)&pktinfo,
- sizeof(pktinfo)) < 0)
- log_fatal("setsockopt: IP_PKTINFO: %m");
+ struct msghdr m;
+ struct iovec v;
+ struct sockaddr_in dst;
+ struct in_pktinfo *pktinfo;
+ struct cmsghdr *cmsg;
+ unsigned int ifindex;
+
+ /*
+ * If necessary allocate space for the control message header.
+ * The space is common between send and receive.
+ */
+
+ if (v4_control_buf == NULL) {
+ v4_allocate_cmsg_cbuf();
+ if (v4_control_buf == NULL) {
+ log_error("send_packet: unable to allocate cmsg header");
+ return(ENOMEM);
}
-#endif
- result = sendto (interface -> wfdesc, (char *)raw, len, 0,
- (struct sockaddr *)to, sizeof *to);
-#ifdef IGNORE_HOSTUNREACH
- } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) &&
- result < 0 &&
- (errno == EHOSTUNREACH ||
- errno == ECONNREFUSED) &&
- retry++ < 10);
-#endif
+ }
+ memset(v4_control_buf, 0, v4_control_buf_len);
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Set the target address we're sending to.
+ */
+ memcpy(&dst, to, sizeof(dst));
+ m.msg_name = &dst;
+ m.msg_namelen = sizeof(dst);
+ ifindex = if_nametoindex(interface->name);
+
+ /*
+ * Set the data buffer we're sending. (Using this wacky
+ * "scatter-gather" stuff... we only have a single chunk
+ * of data to send, so we declare a single vector entry.)
+ */
+ v.iov_base = (char *)raw;
+ v.iov_len = len;
+ m.msg_iov = &v;
+ m.msg_iovlen = 1;
+
+ /*
+ * Setting the interface is a bit more involved.
+ *
+ * We have to create a "control message", and set that to
+ * define the IP packet information. We let he kernel decide
+ * the source IP address unless 'use_src_intf_ip_for_tx' is
+ * set, in which case we use the IP address of the ingress
+ * interface we received the request on as the source IP.
+ */
+ m.msg_control = v4_control_buf;
+ m.msg_controllen = v4_control_buf_len;
+ cmsg = CMSG_FIRSTHDR(&m);
+ INSIST(cmsg != NULL);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+ pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ memset(pktinfo, 0, sizeof(*pktinfo));
+ pktinfo->ipi_ifindex = ifindex;
+ if (use_src_intf_ip_for_tx)
+ pktinfo->ipi_spec_dst = from;
+
+ result = sendmsg(interface->wfdesc, &m, 0);
if (result < 0) {
- log_error ("send_packet: %m");
- if (errno == ENETUNREACH)
- log_error ("send_packet: please consult README file%s",
- " regarding broadcast address.");
+ log_error("send_packet: %m");
}
+
return result;
}

diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index 36cd518..0c25582 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -2660,6 +2660,7 @@ ssize_t send_fallback6(struct interface_info *, struct packet *,
#endif

#ifdef USE_SOCKET_SEND
+extern int use_src_intf_ip_for_tx;
void if_reinitialize_send (struct interface_info *);
void if_register_send (struct interface_info *);
void if_deregister_send (struct interface_info *);
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
index 221106a..c44a79d 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -97,6 +97,12 @@ struct downstream_intf_list {
isc_boolean_t use_if_id = ISC_FALSE;
#endif

+/*
+ * If not set, kernel chooses what the src ip is.
+ * If set, uses "from" interface IP for packet Tx.
+ */
+extern int use_src_intf_ip_for_tx = 0;
+
/* Maximum size of a packet with agent options added. */
int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;

@@ -431,6 +437,8 @@ main(int argc, char **argv) {
#endif
} else if (!strcmp(argv[i], "-d")) {
/* no_daemon = 1; */
+ } else if (!strcmp(argv[i], "-si")) {
+ use_src_intf_ip_for_tx = 1;
} else if (!strcmp(argv[i], "-q")) {
quiet = 1;
quiet_interface_discovery = 1;
--
2.17.1

1 change: 1 addition & 0 deletions src/isc-dhcp/patch/series
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
0009-Support-for-dual-tor-scenario.patch
0010-Bugfix-correctly-set-interface-netmask.patch
0011-dhcp-relay-Prevent-Buffer-Overrun.patch
0012-add-option-si-to-support-using-src-intf-ip-in-relay.patch