DHCP Relay: add option -si to support using src intf ip in relay (#7052)
* add option si to support using src intf ip in relay
This commit is contained in:
parent
a3cafee02c
commit
8f83b33e02
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user