Skip to content

Commit

Permalink
DHCP Relay: add option -si to support using src intf ip in relay (son…
Browse files Browse the repository at this point in the history
…ic-net#7052)

* add option si to support using src intf ip in relay
  • Loading branch information
trzhang-msft authored Mar 19, 2021
1 parent a3cafee commit 8f83b33
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 0 deletions.
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 <trzhang@microsoft.com>
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

0 comments on commit 8f83b33

Please sign in to comment.