Skip to content

Commit

Permalink
[dhcp-relay]: add option -si to support using src intf ip in relay (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
trzhang-msft authored Mar 15, 2021
1 parent a0b824f commit 139fcf5
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
From a89c0ef099852163aaee7c0704c814293063d6f3 Mon Sep 17 00:00:00 2001
From: Tianrong Zhang <trzhang@microsoft.com>
Date: Sun, 14 Mar 2021 17:47:07 -0700
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 e8851b4..ddd1f2e 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -78,6 +78,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
@@ -629,37 +652,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 89bfe82..4f7a71d 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -2611,6 +2611,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 8aac4b3..1316591 100644
--- a/relay/dhcrelay.c
+++ b/relay/dhcrelay.c
@@ -85,6 +85,12 @@ int max_hop_count = 10; /* Maximum hop count */
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;

@@ -317,6 +323,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

3 changes: 2 additions & 1 deletion src/isc-dhcp/patch/series
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
0008-CVE-2017-3144.patch
0009-CVE-2018-5733.patch
0010-CVE-2018-5732.patch
0008-interface-name-maxlen-crash.patch
0011-interface-name-maxlen-crash.patch
0012-Don-t-skip-down-interfaces-when-discovering-interfac.patch
0013-add-option-si-to-support-using-src-intf-ip-in-relay.patch

0 comments on commit 139fcf5

Please sign in to comment.