Skip to content

Commit 017eea8

Browse files
jlevequelguohan
authored andcommitted
[DHCP Relay]: Add support for custom Option 82 circuit_id of the form '<hostname>:<portname>' (#747)
* Add docker-dhcp-relay/Dockerfile to .gitignore * Add isc-dhcp-relay .deb package to image build process, along with my Option 82 patch * Install custom isc-dhcp-relay in dhcp_relay docker * Install isc-dhcp-relay build dependencies in sonic-slave Docker container * Copy the built .deb package to the destination directory * Add dependencies for isc-dhcp-relay * Change Option 82 string to '<hostname>:<portname>' * Install dependencies of .deb files implicitly in Dockerfile * Remove unused line * Remove unnecessary space
1 parent 0cdad94 commit 017eea8

8 files changed

+351
-5
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ src/sonic-device-data/src/device/
2929
dockers/docker-base/Dockerfile
3030
dockers/docker-config-engine/Dockerfile
3131
dockers/docker-database/Dockerfile
32+
dockers/docker-dhcp-relay/Dockerfile
3233
dockers/docker-fpm-frr/Dockerfile
3334
dockers/docker-fpm-gobgp/Dockerfile
3435
dockers/docker-fpm-quagga/Dockerfile

dockers/docker-dhcp-relay/Dockerfile renamed to dockers/docker-dhcp-relay/Dockerfile.j2

+11-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,17 @@ ENV DEBIAN_FRONTEND=noninteractive
66
# Update apt's cache of available packages
77
RUN apt-get update
88

9-
# Install isc-dhcp-relay Debian package
10-
RUN apt-get -y install isc-dhcp-relay
9+
{% if docker_dhcp_relay_debs.strip() -%}
10+
# Copy built Debian packages
11+
{%- for deb in docker_dhcp_relay_debs.split(' ') %}
12+
COPY debs/{{ deb }} debs/
13+
{%- endfor %}
14+
15+
# Install built Debian packages and implicitly install their dependencies
16+
{%- for deb in docker_dhcp_relay_debs.split(' ') %}
17+
RUN dpkg_apt() { [ -f $1 ] && { dpkg -i $1 || apt-get -y install -f; } || return 1; }; dpkg_apt debs/{{ deb }}
18+
{%- endfor %}
19+
{%- endif %}
1120

1221
# Clean up
1322
RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y

dockers/docker-dhcp-relay/isc-dhcp-relay.j2

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ INTERFACES="
2424
{%- endif %}
2525
{%- endfor %}"
2626

27-
# '-a' option provides option 82 circuit id information
28-
OPTIONS="-a"
27+
# '-a' option provides option 82 circuit_id and remote_id information
28+
OPTIONS="-a \"%h:%p\" \"\""
2929

rules/docker-dhcp-relay.mk

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
DOCKER_DHCP_RELAY = docker-dhcp-relay.gz
44
$(DOCKER_DHCP_RELAY)_PATH = $(DOCKERS_PATH)/docker-dhcp-relay
5+
$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_COMMON) $(ISC_DHCP_RELAY)
56
$(DOCKER_DHCP_RELAY)_LOAD_DOCKERS = $(DOCKER_CONFIG_ENGINE)
6-
SONIC_SIMPLE_DOCKER_IMAGES += $(DOCKER_DHCP_RELAY)
7+
SONIC_DOCKER_IMAGES += $(DOCKER_DHCP_RELAY)
78
SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_DHCP_RELAY)
89

910

rules/isc-dhcp.mk

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# isc-dhcp packages
2+
3+
ISC_DHCP_VERSION = 4.3.1-6
4+
5+
export ISC_DHCP_VERSION
6+
7+
ISC_DHCP_COMMON = isc-dhcp-common_$(ISC_DHCP_VERSION)_amd64.deb
8+
$(ISC_DHCP_COMMON)_SRC_PATH = $(SRC_PATH)/isc-dhcp
9+
SONIC_MAKE_DEBS += $(ISC_DHCP_COMMON)
10+
11+
ISC_DHCP_RELAY = isc-dhcp-relay_$(ISC_DHCP_VERSION)_amd64.deb
12+
$(eval $(call add_derived_package,$(ISC_DHCP_COMMON),$(ISC_DHCP_RELAY)))
13+

sonic-slave/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ RUN apt-get update && apt-get install -y \
216216
# For sonic config engine testing
217217
pyangbind
218218

219+
# Install dependencies for building isc-dhcp-relay
220+
RUN apt-get -y build-dep isc-dhcp
221+
219222
RUN cd /usr/src/gtest && cmake . && make -C /usr/src/gtest
220223

221224
RUN mkdir /var/run/sshd

src/isc-dhcp/Makefile

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.ONESHELL:
2+
SHELL = /bin/bash
3+
.SHELLFLAGS += -e
4+
5+
MAIN_TARGET = isc-dhcp-common_$(ISC_DHCP_VERSION)_amd64.deb
6+
DERIVED_TARGETS = isc-dhcp-relay_$(ISC_DHCP_VERSION)_amd64.deb
7+
8+
$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
9+
# Remove any stale files
10+
rm -rf ./isc-dhcp
11+
12+
# Clone isc-dhcp repo
13+
git clone git://anonscm.debian.org/pkg-dhcp/isc-dhcp.git
14+
pushd ./isc-dhcp
15+
git checkout -f debian/4.3.1-6
16+
popd
17+
18+
# Apply patch
19+
patch -p1 < isc-dhcp-4.3.1_dhcrelay-custom-circuit_id-remote_id-and-bridge-iface-support.patch
20+
21+
# Build source and Debian packages
22+
pushd ./isc-dhcp
23+
dpkg-buildpackage -rfakeroot -b -us -uc
24+
popd
25+
26+
# Move the newly-built .deb packages to the destination directory
27+
mv $* $(DERIVED_TARGETS) $(DEST)/
28+
29+
$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
This patch adds the following functionality to dhcrelay in isc-dhcp v4.3.1-6:
2+
* Add customizable Circuit ID and Remote ID fields
3+
* Support for obtaining name of physical interface of interfaces that are part of a bridge interface
4+
5+
diff -ruN a/isc-dhcp/relay/dhcrelay.c b/isc-dhcp/relay/dhcrelay.c
6+
--- a/isc-dhcp/relay/dhcrelay.c 2014-08-06 22:35:02.000000000 +0000
7+
+++ b/isc-dhcp/relay/dhcrelay.c 2017-06-08 21:39:53.856192546 +0000
8+
@@ -73,6 +73,8 @@
9+
did not match any known circuit ID. */
10+
int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
11+
was missing. */
12+
+const char *agent_circuit_id_fmt = NULL; /* Circuit ID custom format string. */
13+
+const char *agent_remote_id_fmt = NULL; /* Remote ID custom format string. */
14+
int max_hop_count = 10; /* Maximum hop count */
15+
16+
#ifdef DHCPv6
17+
@@ -140,9 +142,19 @@
18+
static const char url[] =
19+
"For info, please visit https://www.isc.org/software/dhcp/";
20+
21+
+#define DHCRELAY_OPTION82_USAGE \
22+
+"circuit_id/remote_id interpreted sequences are:\n" \
23+
+"\n" \
24+
+" %%%% A single %%\n" \
25+
+" %%h Hostname of device\n" \
26+
+" %%p Name of interface that generated the request\n" \
27+
+" %%P Hardware address of interface that generated the request\n" \
28+
+" %%C Client hardware address\n" \
29+
+" %%I DHCP relay agent IP Address\n" \
30+
+
31+
#ifdef DHCPv6
32+
#define DHCRELAY_USAGE \
33+
-"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
34+
+"Usage: dhcrelay [-4] [-d] [-q] [-a <circuit_id> <remote_id>] [-D]\n"\
35+
" [-A <length>] [-c <hops>] [-p <port>]\n" \
36+
" [-pf <pid-file>] [--no-pid]\n"\
37+
" [-m append|replace|forward|discard]\n" \
38+
@@ -154,14 +166,15 @@
39+
" -l lower0 [ ... -l lowerN]\n" \
40+
" -u upper0 [ ... -u upperN]\n" \
41+
" lower (client link): [address%%]interface[#index]\n" \
42+
-" upper (server link): [address%%]interface"
43+
+" upper (server link): [address%%]interface\n\n" DHCRELAY_OPTION82_USAGE
44+
#else
45+
#define DHCRELAY_USAGE \
46+
-"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
47+
-" [-pf <pid-file>] [--no-pid]\n" \
48+
+"Usage: dhcrelay [-d] [-q] [-a <circuit_id> <remote_id>] [-D]\n" \
49+
+" [-A <length>] [-c <hops>] [-p <port>]\n" \
50+
+" [-pf <pid-file>] [--no-pid]\n"\
51+
" [-m append|replace|forward|discard]\n" \
52+
" [-i interface0 [ ... -i interfaceN]\n" \
53+
-" server0 [ ... serverN]\n\n"
54+
+" server0 [ ... serverN]\n\n" DHCRELAY_OPTION82_USAGE
55+
#endif
56+
57+
static void usage() {
58+
@@ -287,6 +300,15 @@
59+
local_family_set = 1;
60+
local_family = AF_INET;
61+
#endif
62+
+ if (++i == argc)
63+
+ usage();
64+
+
65+
+ if (argv[i] != NULL && argv[i][0] != '-')
66+
+ agent_circuit_id_fmt = argv[i++];
67+
+
68+
+ if (argv[i] != NULL && argv[i][0] != '-')
69+
+ agent_remote_id_fmt = argv[i];
70+
+
71+
add_agent_options = 1;
72+
} else if (!strcmp(argv[i], "-A")) {
73+
#ifdef DHCPv6
74+
@@ -937,6 +959,166 @@
75+
return (-1);
76+
}
77+
78+
+static int
79+
+_bridgefdbquery(const char *hwAddr, char *interface, int *vlanid) {
80+
+
81+
+#define xstr(s) str(s)
82+
+#define str(s) #s
83+
+#define FDB_STRING_LEN 100
84+
+#define FDB_BUFFER_LEN (FDB_STRING_LEN + 1)
85+
+
86+
+/*
87+
+ * Format for sscanf() to read the 1st, 3th, and 5th
88+
+ * space-delimited fields
89+
+ *
90+
+ * bridge fdb show output
91+
+ * 6c:64:1a:00:06:13 dev swp35 vlan 0 master bridge permanent
92+
+ */
93+
+#define FDB_LINE_FORMAT "%" xstr(FDB_STRING_LEN) "s %*s " \
94+
+ "%" xstr(FDB_STRING_LEN) "s %*s %d %*s"
95+
+
96+
+ char cmdstr[FDB_BUFFER_LEN];
97+
+ char buf[FDB_BUFFER_LEN];
98+
+ char macAddr[FDB_BUFFER_LEN];
99+
+
100+
+ if ((interface == NULL) || (vlanid == NULL)) {
101+
+ return 0;
102+
+ }
103+
+ sprintf(cmdstr, "bridge fdb show | grep -m 1 %s", hwAddr);
104+
+ FILE *cmd = popen(cmdstr, "r");
105+
+
106+
+ if (cmd != NULL) {
107+
+ while (fgets(buf, sizeof(buf), cmd)) {
108+
+ sscanf(buf, FDB_LINE_FORMAT, macAddr, interface, vlanid);
109+
+ log_debug ("bridgefdbquery: macAddr:%s interface: %s vlanid %d",
110+
+ macAddr,
111+
+ interface, *vlanid);
112+
+ }
113+
+ pclose(cmd);
114+
+ return 0;
115+
+ }
116+
+
117+
+ return -1;
118+
+}
119+
+
120+
+/*
121+
+ * Format the message that will be used by circuit_id and remote_id
122+
+ */
123+
+static int
124+
+format_relay_agent_rfc3046_msg(struct interface_info *ip, struct dhcp_packet *packet,
125+
+ const char *format, char *msg, size_t msgn) {
126+
+ size_t len = 0;
127+
+ char hostname[HOST_NAME_MAX + 1] = { 0 };
128+
+ char ifname[IFNAMSIZ + 1] = { 0 };
129+
+ char *buf = msg;
130+
+
131+
+ for ( ; format && *format && len < msgn; ++format) {
132+
+ size_t strn = 0;
133+
+ const char *str = NULL;
134+
+
135+
+ if (*format == '%') {
136+
+ switch (*++format) {
137+
+ case '\0':
138+
+ --format;
139+
+ break;
140+
+
141+
+ case '%': /* A literal '%' */
142+
+ str = "%";
143+
+ break;
144+
+
145+
+ case 'h': /* Hostname */
146+
+ gethostname(hostname, HOST_NAME_MAX);
147+
+ str = hostname;
148+
+ break;
149+
+
150+
+ case 'p': /* Name of interface that we received the request from */
151+
+ /*
152+
+ * Query FDB to identify the exact physical interface only when source MAC address
153+
+ * is present and '20: DHCP relay agent IP address' (giaddr) is not present
154+
+ */
155+
+ if (packet->htype && !packet->giaddr.s_addr) {
156+
+ int ret = 0, vlanid = 0;
157+
+
158+
+ ret = _bridgefdbquery(print_hw_addr(packet->htype, packet->hlen, packet->chaddr),
159+
+ ip->name,
160+
+ &vlanid);
161+
+
162+
+ if (ret < 0) {
163+
+ log_debug("MAC Address: %s (interface:%s vlan:%d) not found in bridge fdb show",
164+
+ print_hw_addr (packet->htype, packet->hlen, packet->chaddr),
165+
+ ip->name,
166+
+ vlanid);
167+
+ strncpy(ifname, ip->name, IFNAMSIZ);
168+
+ }
169+
+ else if (strlen(ip->name) > 0) {
170+
+ char cmdstr[256] = { 0 };
171+
+ char cmdout[256] = { 0 };
172+
+
173+
+ log_debug("Adding option 82 interface name for MAC Address: %s as %s",
174+
+ print_hw_addr (packet->htype, packet->hlen, packet->chaddr),
175+
+ ip->name);
176+
+
177+
+ // Translate SONiC interface name to vendor alias
178+
+ sprintf(cmdstr, "sonic-cfggen -m /etc/sonic/minigraph.xml -v \"minigraph_ports['%s'].alias\"", ip->name);
179+
+
180+
+ FILE *cmd = popen(cmdstr, "r");
181+
+
182+
+ if (cmd != NULL) {
183+
+ while (fgets(cmdout, sizeof(cmdout), cmd)) {
184+
+ // Strip any trailing newline
185+
+ if (cmdout[strlen(cmdout) - 1] == '\n')
186+
+ cmdout[strlen(cmdout) - 1] = '\0';
187+
+
188+
+ log_debug ("Retrieved alias %s for interface %s", buf, ip->name);
189+
+ }
190+
+
191+
+ pclose(cmd);
192+
+ }
193+
+
194+
+ strncpy(ifname, cmdout, IFNAMSIZ);
195+
+ }
196+
+
197+
+ str = ifname;
198+
+ }
199+
+ break;
200+
+
201+
+ case 'P': /* Physical address of interface that we received the request from */
202+
+ str = print_hw_addr(ip->hw_address.hbuf[0], ip->hw_address.hlen - 1, &ip->hw_address.hbuf[1]);
203+
+ break;
204+
+
205+
+ case 'C': /* 24: Client hardware address */
206+
+ str = print_hw_addr(packet->htype, packet->hlen, packet->chaddr);
207+
+ break;
208+
+
209+
+ case 'I': /* 20: DHCP relay agent IP address */
210+
+ str = inet_ntoa(packet->giaddr);
211+
+ break;
212+
+
213+
+ default:
214+
+ log_error("Option %%%c is unrecognized and will not be formatted!", *format);
215+
+ continue;
216+
+ }
217+
+
218+
+ if (str)
219+
+ strn = strlen(str);
220+
+ } else {
221+
+ str = format;
222+
+ strn += 1;
223+
+ }
224+
+
225+
+ // Do we have room?
226+
+ if ((strn+len) > msgn) {
227+
+ return 0;
228+
+ }
229+
+
230+
+ memcpy(buf+len, str, strn);
231+
+ len += strn;
232+
+ }
233+
+
234+
+ return len;
235+
+}
236+
+
237+
+
238+
/*
239+
* Examine a packet to see if it's a candidate to have a Relay
240+
* Agent Information option tacked onto its tail. If it is, tack
241+
@@ -948,6 +1130,8 @@
242+
int is_dhcp = 0, mms;
243+
unsigned optlen;
244+
u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
245+
+ char circuit_id_buf[255] = { '\0', };
246+
+ char remote_id_buf[255] = { '\0', };
247+
248+
/* If we're not adding agent options to packets, we can skip
249+
this. */
250+
@@ -1077,6 +1261,38 @@
251+
op = sp;
252+
#endif
253+
254+
+ /* option82: custom string for circuit_id */
255+
+ if (agent_circuit_id_fmt) {
256+
+ size_t len = 0;
257+
+
258+
+ len = format_relay_agent_rfc3046_msg(ip, packet, agent_circuit_id_fmt,
259+
+ circuit_id_buf, sizeof(circuit_id_buf));
260+
+
261+
+ if (len > 0) {
262+
+ ip->circuit_id = (uint8_t *)circuit_id_buf;
263+
+ ip->circuit_id_len = len;
264+
+
265+
+ log_debug("sending on %s option82:circuit_id='%s'(%d)",
266+
+ ip->name, (char *)ip->circuit_id, ip->circuit_id_len);
267+
+ }
268+
+ }
269+
+
270+
+ /* option82: custom string for remote_id */
271+
+ if (agent_remote_id_fmt) {
272+
+ size_t len = 0;
273+
+
274+
+ len = format_relay_agent_rfc3046_msg(ip, packet, agent_remote_id_fmt,
275+
+ remote_id_buf, sizeof(remote_id_buf));
276+
+
277+
+ if (len > 0) {
278+
+ ip->remote_id = (uint8_t *)remote_id_buf;
279+
+ ip->remote_id_len = len;
280+
+
281+
+ log_debug("sending on %s option82:remote_id='%s'(%d)",
282+
+ ip->name, (char *)ip->remote_id, ip->remote_id_len);
283+
+ }
284+
+ }
285+
+
286+
/* Sanity check. Had better not ever happen. */
287+
if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
288+
log_fatal("Circuit ID length %d out of range [1-255] on "
289+

0 commit comments

Comments
 (0)