Merge ~fnordahl/ubuntu/+source/ovn:bug/1857026-focal into ~ubuntu-server-dev/ubuntu/+source/ovn:ubuntu/focal

Proposed by Frode Nordahl
Status: Merged
Merged at revision: 1272554ada5e7ecfcc9b090d696d7b092779bdb6
Proposed branch: ~fnordahl/ubuntu/+source/ovn:bug/1857026-focal
Merge into: ~ubuntu-server-dev/ubuntu/+source/ovn:ubuntu/focal
Diff against target: 803 lines (+775/-0)
4 files modified
debian/changelog (+8/-0)
debian/patches/lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch (+274/-0)
debian/patches/lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch (+491/-0)
debian/patches/series (+2/-0)
Reviewer Review Type Date Requested Status
James Page Pending
Review via email: mp+413737@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Frode Nordahl (fnordahl) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index c0af91d..2094609 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
1ovn (20.03.2-0ubuntu0.20.04.3) focal; urgency=medium
2
3 * Add support for PTR DNS requests (LP: #1857026)
4 - d/p/lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch
5 - d/p/lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch
6
7 -- Frode Nordahl <frode.nordahl@canonical.com> Thu, 06 Jan 2022 10:00:00 +0100
8
1ovn (20.03.2-0ubuntu0.20.04.2) focal; urgency=medium9ovn (20.03.2-0ubuntu0.20.04.2) focal; urgency=medium
210
3 * Add RBAC rules for IGMP_Group table (LP: #1914988):11 * Add RBAC rules for IGMP_Group table (LP: #1914988):
diff --git a/debian/patches/lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch b/debian/patches/lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch
4new file mode 10064412new file mode 100644
index 0000000..bfb394a
--- /dev/null
+++ b/debian/patches/lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch
@@ -0,0 +1,274 @@
1Origin: backport, https://github.com/ovn-org/ovn/commit/9287f425e8bc5781728b2ff1c60413d3c39c33a8
2Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ovn/+bug/1857026
3Last-Update: 2022-01-06
4
5From 815d4c04992d994bcd75b3d642fe6cc694ec99b3 Mon Sep 17 00:00:00 2001
6From: Mark Michelson <mmichels@redhat.com>
7Date: Mon, 20 Apr 2020 09:25:09 -0400
8Subject: [PATCH 1/2] DNS: Make DNS lookups case insensitive.
9
10From RFC 1035 Section 2.3.3:
11
12"For all parts of the DNS that are part of the official protocol, all
13comparisons between character strings (e.g., labels, domain names, etc.)
14are done in a case-insensitive manner."
15
16OVN was using case-sensitive lookups and therefore was not complying.
17This change makes lookups case insensitive by storing lowercase record
18names in the southbound database and converting incoming query names to
19lowercase.
20
21Signed-off-by: Mark Michelson <mmichels@redhat.com>
22Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819069
23Reported-by: Jianlin Shi <jishi@redhat.com>
24Acked-by: Numan Siddique <numans@ovn.org>
25(cherry picked from commit 9287f425e8bc5781728b2ff1c60413d3c39c33a8)
26---
27 controller/pinctrl.c | 7 ++++-
28 lib/ovn-util.c | 15 +++++++++++
29 lib/ovn-util.h | 5 ++++
30 northd/ovn-northd.c | 15 ++++++++++-
31 ovn-sb.xml | 3 ++-
32 tests/ovn.at | 61 ++++++++++++++++++++++++++++++++------------
33 6 files changed, 87 insertions(+), 19 deletions(-)
34
35diff --git a/controller/pinctrl.c b/controller/pinctrl.c
36index 5822f03ef..4879dcc45 100644
37--- a/controller/pinctrl.c
38+++ b/controller/pinctrl.c
39@@ -1767,7 +1767,12 @@ pinctrl_handle_dns_lookup(
40 struct dns_data *d = iter->data;
41 for (size_t i = 0; i < d->n_dps; i++) {
42 if (d->dps[i] == dp_key) {
43- answer_ips = smap_get(&d->records, ds_cstr(&query_name));
44+ /* DNS records in SBDB are stored in lowercase. Convert to
45+ * lowercase to perform case insensitive lookup
46+ */
47+ char *query_name_lower = str_tolower(ds_cstr(&query_name));
48+ answer_ips = smap_get(&d->records, query_name_lower);
49+ free(query_name_lower);
50 if (answer_ips) {
51 break;
52 }
53diff --git a/lib/ovn-util.c b/lib/ovn-util.c
54index 514e2489f..1b30c2e9a 100644
55--- a/lib/ovn-util.c
56+++ b/lib/ovn-util.c
57@@ -21,6 +21,7 @@
58 #include "openvswitch/ofp-parse.h"
59 #include "ovn-nb-idl.h"
60 #include "ovn-sb-idl.h"
61+#include <ctype.h>
62
63 VLOG_DEFINE_THIS_MODULE(ovn_util);
64
65@@ -550,3 +551,17 @@ ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2)
66 (addr1->family == AF_INET ? addr1->ipv4 == addr2->ipv4 :
67 IN6_ARE_ADDR_EQUAL(&addr1->ipv6, &addr2->ipv6)));
68 }
69+
70+char *
71+str_tolower(const char *orig)
72+{
73+ char *copy = xmalloc(strlen(orig) + 1);
74+ char *p = copy;
75+
76+ while (*orig) {
77+ *p++ = tolower(*orig++);
78+ }
79+ *p = '\0';
80+
81+ return copy;
82+}
83diff --git a/lib/ovn-util.h b/lib/ovn-util.h
84index 11238f61c..4076e8b9a 100644
85--- a/lib/ovn-util.h
86+++ b/lib/ovn-util.h
87@@ -124,4 +124,9 @@ struct v46_ip {
88 bool ip46_parse_cidr(const char *str, struct v46_ip *prefix,
89 unsigned int *plen);
90 bool ip46_equals(const struct v46_ip *addr1, const struct v46_ip *addr2);
91+
92+/* Returns a lowercase copy of orig.
93+ * Caller must free the returned string.
94+ */
95+char *str_tolower(const char *orig);
96 #endif
97diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
98index 9cc2c04bb..4ced1b48f 100644
99--- a/northd/ovn-northd.c
100+++ b/northd/ovn-northd.c
101@@ -10634,7 +10634,20 @@ sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
102 dns_info->sb_dns,
103 (struct sbrec_datapath_binding **)dns_info->sbs,
104 dns_info->n_sbs);
105- sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
106+
107+ /* DNS lookups are case-insensitive. Convert records to lowercase so
108+ * we can do consistent lookups when DNS requests arrive
109+ */
110+ struct smap lower_records = SMAP_INITIALIZER(&lower_records);
111+ struct smap_node *node;
112+ SMAP_FOR_EACH (node, &dns_info->nb_dns->records) {
113+ smap_add_nocopy(&lower_records, xstrdup(node->key),
114+ str_tolower(node->value));
115+ }
116+
117+ sbrec_dns_set_records(dns_info->sb_dns, &lower_records);
118+
119+ smap_destroy(&lower_records);
120 free(dns_info->sbs);
121 free(dns_info);
122 }
123diff --git a/ovn-sb.xml b/ovn-sb.xml
124index 2703e6d0c..64c33d2df 100644
125--- a/ovn-sb.xml
126+++ b/ovn-sb.xml
127@@ -3581,7 +3581,8 @@ tcp.flags = RST;
128 <column name="records">
129 Key-value pair of DNS records with <code>DNS query name</code> as the key
130 and a string of IP address(es) separated by comma or space as the
131- value.
132+ value. ovn-northd stores the DNS query name in all lowercase in order to
133+ facilitate case-insensitive lookups.
134
135 <p><b>Example: </b> "vm1.ovn.org" = "10.0.0.4 aef0::4"</p>
136 </column>
137diff --git a/tests/ovn.at b/tests/ovn.at
138index 4d9ee1256..b6eff4349 100644
139--- a/tests/ovn.at
140+++ b/tests/ovn.at
141@@ -8317,6 +8317,12 @@ set_dns_params() {
142 # IPv4 address - 10.0.0.4
143 expected_dns_answer=${query_name}00010001${ttl}00040a000004
144 ;;
145+ VM1)
146+ # VM1.OVN.ORG
147+ query_name=03564d31034f564e034f524700
148+ # IPv4 address - 10.0.0.4
149+ expected_dns_answer=${query_name}00010001${ttl}00040a000004
150+ ;;
151 vm2)
152 # vm2.ovn.org
153 query_name=03766d32036f766e036f726700
154@@ -8479,6 +8485,29 @@ reset_pcap_file hv1-vif2 hv1/vif2
155 rm -f 1.expected
156 rm -f 2.expected
157
158+# Try vm1 again but an all-caps query name
159+
160+set_dns_params VM1
161+src_ip=`ip_to_hex 10 0 0 6`
162+dst_ip=`ip_to_hex 10 0 0 1`
163+dns_reply=1
164+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
165+
166+# NXT_RESUMEs should be 3.
167+OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
168+
169+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
170+cat 2.expected | cut -c -48 > expout
171+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
172+# Skipping the IPv4 checksum.
173+cat 2.expected | cut -c 53- > expout
174+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
175+
176+reset_pcap_file hv1-vif1 hv1/vif1
177+reset_pcap_file hv1-vif2 hv1/vif2
178+rm -f 1.expected
179+rm -f 2.expected
180+
181 # Clear the query name options for ls1-lp2
182 ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
183
184@@ -8488,8 +8517,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
185 dns_reply=0
186 test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
187
188-# NXT_RESUMEs should be 3.
189-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
190+# NXT_RESUMEs should be 4.
191+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
192
193 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
194 AT_CHECK([cat 1.packets], [0], [])
195@@ -8510,8 +8539,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
196 dns_reply=0
197 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
198
199-# NXT_RESUMEs should be 3 only.
200-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
201+# NXT_RESUMEs should be 4 only.
202+OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
203
204 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
205 AT_CHECK([cat 2.packets], [0], [])
206@@ -8531,8 +8560,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
207 dns_reply=1
208 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
209
210-# NXT_RESUMEs should be 4.
211-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
212+# NXT_RESUMEs should be 5.
213+OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
214
215 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
216 cat 2.expected | cut -c -48 > expout
217@@ -8553,8 +8582,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
218 dns_reply=1
219 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
220
221-# NXT_RESUMEs should be 5.
222-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
223+# NXT_RESUMEs should be 6.
224+OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
225
226 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
227 cat 2.expected | cut -c -48 > expout
228@@ -8575,8 +8604,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
229 dns_reply=0
230 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
231
232-# NXT_RESUMEs should be 6.
233-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
234+# NXT_RESUMEs should be 7.
235+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
236
237 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
238 AT_CHECK([cat 2.packets], [0], [])
239@@ -8593,8 +8622,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
240 dns_reply=0
241 test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
242
243-# NXT_RESUMEs should be 7.
244-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
245+# NXT_RESUMEs should be 8.
246+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
247
248 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
249 AT_CHECK([cat 2.packets], [0], [])
250@@ -8613,8 +8642,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
251 dns_reply=1
252 test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
253
254-# NXT_RESUMEs should be 8.
255-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
256+# NXT_RESUMEs should be 9.
257+OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
258
259 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
260 cat 1.expected | cut -c -48 > expout
261@@ -8635,8 +8664,8 @@ dst_ip=aef00000000000000000000000000001
262 dns_reply=1
263 test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
264
265-# NXT_RESUMEs should be 9.
266-OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
267+# NXT_RESUMEs should be 10
268+OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
269
270 $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
271 # Skipping the UDP checksum.
272--
2732.33.1
274
diff --git a/debian/patches/lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch b/debian/patches/lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch
0new file mode 100644275new file mode 100644
index 0000000..4386c6c
--- /dev/null
+++ b/debian/patches/lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch
@@ -0,0 +1,491 @@
1Origin: backport, https://github.com/ovn-org/ovn/commit/82a4e44e308171cb545211eb2534475ef16a4c0e
2Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ovn/+bug/1857026
3Last-Update: 2022-01-06
4
5From e612a270f789af616e98cc204eee42b8b114de41 Mon Sep 17 00:00:00 2001
6From: Vladislav Odintsov <odivlad@gmail.com>
7Date: Fri, 19 Feb 2021 11:57:59 +0300
8Subject: [PATCH 2/2] controller: Add support for PTR DNS requests.
9
10The native OVN DNS support doesn't yet support for PTR DNS requests.
11This patch adds the support for it. If suppose there is a dns record
12as - "vm1.ovn.org"="10.0.0.4", then a normal DNS request will query for
13"vm1.ovn.org" and the reply will be the IP address - 10.0.0.4.
14PTR DNS request helps in getting the domain name of the IP address.
15For the above example, the PTR DNS request will have a query name as
16- "4.0.0.10.in-addr.arpa". And the response will have "vm1.ovn.org".
17In order to support this feature, this patch expects the CMS to define
18an another entry in the DNS record as - "4.0.0.10.in-addr.arpa"="vm1.ovn.org".
19
20This makes the job of ovn-controller easier to support this feature.
21
22Submitted-at: https://github.com/ovn-org/ovn/pull/74
23Signed-off-by: Vladislav Odintsov <odivlad@gmail.com>
24Signed-off-by: Numan Siddique <numans@ovn.org>
25(cherry picked from commit 82a4e44e308171cb545211eb2534475ef16a4c0e)
26---
27 controller/pinctrl.c | 181 +++++++++++++++++++++++++++++++------------
28 lib/ovn-l7.h | 8 ++
29 ovn-nb.xml | 6 ++
30 tests/ovn.at | 83 +++++++++++++++++++-
31 4 files changed, 228 insertions(+), 50 deletions(-)
32
33diff --git a/controller/pinctrl.c b/controller/pinctrl.c
34index 4879dcc45..c505077e0 100644
35--- a/controller/pinctrl.c
36+++ b/controller/pinctrl.c
37@@ -1658,6 +1658,106 @@ destroy_dns_cache(void)
38 }
39 }
40
41+/* Populates dns_answer struct with base data.
42+ * Copy the answer section
43+ * Format of the answer section is
44+ * - NAME -> The domain name
45+ * - TYPE -> 2 octets containing one of the RR type codes
46+ * - CLASS -> 2 octets which specify the class of the data
47+ * in the RDATA field.
48+ * - TTL -> 32 bit unsigned int specifying the time
49+ * interval (in secs) that the resource record
50+ * may be cached before it should be discarded.
51+ * - RDLENGTH -> 16 bit integer specifying the length of the
52+ * RDATA field.
53+ * - RDATA -> a variable length string of octets that
54+ * describes the resource.
55+ */
56+static void
57+dns_build_base_answer(
58+ struct ofpbuf *dns_answer, const uint8_t *in_queryname,
59+ uint16_t query_length, int query_type)
60+{
61+ ofpbuf_put(dns_answer, in_queryname, query_length);
62+ put_be16(dns_answer, htons(query_type));
63+ put_be16(dns_answer, htons(DNS_CLASS_IN));
64+ put_be32(dns_answer, htonl(DNS_DEFAULT_RR_TTL));
65+}
66+
67+/* Populates dns_answer struct with a TYPE A answer. */
68+static void
69+dns_build_a_answer(
70+ struct ofpbuf *dns_answer, const uint8_t *in_queryname,
71+ uint16_t query_length, const ovs_be32 addr)
72+{
73+ dns_build_base_answer(dns_answer, in_queryname, query_length,
74+ DNS_QUERY_TYPE_A);
75+ put_be16(dns_answer, htons(sizeof(ovs_be32)));
76+ put_be32(dns_answer, addr);
77+}
78+
79+/* Populates dns_answer struct with a TYPE AAAA answer. */
80+static void
81+dns_build_aaaa_answer(
82+ struct ofpbuf *dns_answer, const uint8_t *in_queryname,
83+ uint16_t query_length, const struct in6_addr *addr)
84+{
85+ dns_build_base_answer(dns_answer, in_queryname, query_length,
86+ DNS_QUERY_TYPE_AAAA);
87+ put_be16(dns_answer, htons(sizeof(*addr)));
88+ ofpbuf_put(dns_answer, addr, sizeof(*addr));
89+}
90+
91+/* Populates dns_answer struct with a TYPE PTR answer. */
92+static void
93+dns_build_ptr_answer(
94+ struct ofpbuf *dns_answer, const uint8_t *in_queryname,
95+ uint16_t query_length, const char *answer_data)
96+{
97+ char *encoded_answer;
98+ uint16_t encoded_answer_length;
99+
100+ dns_build_base_answer(dns_answer, in_queryname, query_length,
101+ DNS_QUERY_TYPE_PTR);
102+
103+ /* Initialize string 2 chars longer than real answer:
104+ * first label length and terminating zero-length label.
105+ * If the answer_data is - vm1tst.ovn.org, it will be encoded as
106+ * - 0010 (Total length which is 16)
107+ * - 06766d31747374 (vm1tst)
108+ * - 036f766e (ovn)
109+ * - 036f7267 (org
110+ * - 00 (zero length field) */
111+ encoded_answer_length = strlen(answer_data) + 2;
112+ encoded_answer = (char *)xzalloc(encoded_answer_length);
113+
114+ put_be16(dns_answer, htons(encoded_answer_length));
115+ uint8_t label_len_index = 0;
116+ uint16_t label_len = 0;
117+ char *encoded_answer_ptr = (char *)encoded_answer + 1;
118+ while (*answer_data) {
119+ if (*answer_data == '.') {
120+ /* Label has ended. Update the length of the label. */
121+ encoded_answer[label_len_index] = label_len;
122+ label_len_index += (label_len + 1);
123+ label_len = 0; /* Init to 0 for the next label. */
124+ } else {
125+ *encoded_answer_ptr = *answer_data;
126+ label_len++;
127+ }
128+ encoded_answer_ptr++;
129+ answer_data++;
130+ }
131+
132+ /* This is required for the last label if it doesn't end with '.' */
133+ if (label_len) {
134+ encoded_answer[label_len_index] = label_len;
135+ }
136+
137+ ofpbuf_put(dns_answer, encoded_answer, encoded_answer_length);
138+ free(encoded_answer);
139+}
140+
141 /* Called with in the pinctrl_handler thread context. */
142 static void
143 pinctrl_handle_dns_lookup(
144@@ -1753,15 +1853,16 @@ pinctrl_handle_dns_lookup(
145 }
146
147 uint16_t query_type = ntohs(*ALIGNED_CAST(const ovs_be16 *, in_dns_data));
148- /* Supported query types - A, AAAA and ANY */
149+ /* Supported query types - A, AAAA, ANY and PTR */
150 if (!(query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_AAAA
151- || query_type == DNS_QUERY_TYPE_ANY)) {
152+ || query_type == DNS_QUERY_TYPE_ANY
153+ || query_type == DNS_QUERY_TYPE_PTR)) {
154 ds_destroy(&query_name);
155 goto exit;
156 }
157
158 uint64_t dp_key = ntohll(pin->flow_metadata.flow.metadata);
159- const char *answer_ips = NULL;
160+ const char *answer_data = NULL;
161 struct shash_node *iter;
162 SHASH_FOR_EACH (iter, &dns_cache) {
163 struct dns_data *d = iter->data;
164@@ -1771,76 +1872,58 @@ pinctrl_handle_dns_lookup(
165 * lowercase to perform case insensitive lookup
166 */
167 char *query_name_lower = str_tolower(ds_cstr(&query_name));
168- answer_ips = smap_get(&d->records, query_name_lower);
169+ answer_data = smap_get(&d->records, query_name_lower);
170 free(query_name_lower);
171- if (answer_ips) {
172+ if (answer_data) {
173 break;
174 }
175 }
176 }
177
178- if (answer_ips) {
179+ if (answer_data) {
180 break;
181 }
182 }
183
184 ds_destroy(&query_name);
185- if (!answer_ips) {
186+ if (!answer_data) {
187 goto exit;
188 }
189
190- struct lport_addresses ip_addrs;
191- if (!extract_ip_addresses(answer_ips, &ip_addrs)) {
192- goto exit;
193- }
194
195 uint16_t ancount = 0;
196 uint64_t dns_ans_stub[128 / 8];
197 struct ofpbuf dns_answer = OFPBUF_STUB_INITIALIZER(dns_ans_stub);
198
199- if (query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_ANY) {
200- for (size_t i = 0; i < ip_addrs.n_ipv4_addrs; i++) {
201- /* Copy the answer section */
202- /* Format of the answer section is
203- * - NAME -> The domain name
204- * - TYPE -> 2 octets containing one of the RR type codes
205- * - CLASS -> 2 octets which specify the class of the data
206- * in the RDATA field.
207- * - TTL -> 32 bit unsigned int specifying the time
208- * interval (in secs) that the resource record
209- * may be cached before it should be discarded.
210- * - RDLENGTH -> 16 bit integer specifying the length of the
211- * RDATA field.
212- * - RDATA -> a variable length string of octets that
213- * describes the resource. In our case it will
214- * be IP address of the domain name.
215- */
216- ofpbuf_put(&dns_answer, in_queryname, idx);
217- put_be16(&dns_answer, htons(DNS_QUERY_TYPE_A));
218- put_be16(&dns_answer, htons(DNS_CLASS_IN));
219- put_be32(&dns_answer, htonl(DNS_DEFAULT_RR_TTL));
220- put_be16(&dns_answer, htons(sizeof(ovs_be32)));
221- put_be32(&dns_answer, ip_addrs.ipv4_addrs[i].addr);
222- ancount++;
223+ if (query_type == DNS_QUERY_TYPE_PTR) {
224+ dns_build_ptr_answer(&dns_answer, in_queryname, idx, answer_data);
225+ ancount++;
226+ } else {
227+ struct lport_addresses ip_addrs;
228+ if (!extract_ip_addresses(answer_data, &ip_addrs)) {
229+ goto exit;
230+ }
231+
232+ if (query_type == DNS_QUERY_TYPE_A ||
233+ query_type == DNS_QUERY_TYPE_ANY) {
234+ for (size_t i = 0; i < ip_addrs.n_ipv4_addrs; i++) {
235+ dns_build_a_answer(&dns_answer, in_queryname, idx,
236+ ip_addrs.ipv4_addrs[i].addr);
237+ ancount++;
238+ }
239 }
240- }
241
242- if (query_type == DNS_QUERY_TYPE_AAAA ||
243- query_type == DNS_QUERY_TYPE_ANY) {
244- for (size_t i = 0; i < ip_addrs.n_ipv6_addrs; i++) {
245- ofpbuf_put(&dns_answer, in_queryname, idx);
246- put_be16(&dns_answer, htons(DNS_QUERY_TYPE_AAAA));
247- put_be16(&dns_answer, htons(DNS_CLASS_IN));
248- put_be32(&dns_answer, htonl(DNS_DEFAULT_RR_TTL));
249- const struct in6_addr *ip6 = &ip_addrs.ipv6_addrs[i].addr;
250- put_be16(&dns_answer, htons(sizeof *ip6));
251- ofpbuf_put(&dns_answer, ip6, sizeof *ip6);
252- ancount++;
253+ if (query_type == DNS_QUERY_TYPE_AAAA ||
254+ query_type == DNS_QUERY_TYPE_ANY) {
255+ for (size_t i = 0; i < ip_addrs.n_ipv6_addrs; i++) {
256+ dns_build_aaaa_answer(&dns_answer, in_queryname, idx,
257+ &ip_addrs.ipv6_addrs[i].addr);
258+ ancount++;
259+ }
260 }
261+ destroy_lport_addresses(&ip_addrs);
262 }
263
264- destroy_lport_addresses(&ip_addrs);
265-
266 if (!ancount) {
267 ofpbuf_uninit(&dns_answer);
268 goto exit;
269diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
270index 507949c28..899162866 100644
271--- a/lib/ovn-l7.h
272+++ b/lib/ovn-l7.h
273@@ -26,6 +26,14 @@
274 #include "hash.h"
275 #include "ovn/logical-fields.h"
276
277+#define DNS_QUERY_TYPE_A 0x01
278+#define DNS_QUERY_TYPE_AAAA 0x1c
279+#define DNS_QUERY_TYPE_ANY 0xff
280+#define DNS_QUERY_TYPE_PTR 0x0c
281+
282+#define DNS_CLASS_IN 0x01
283+#define DNS_DEFAULT_RR_TTL 3600
284+
285 /* Generic options map which is used to store dhcpv4 opts and dhcpv6 opts. */
286 struct gen_opts_map {
287 struct hmap_node hmap_node;
288diff --git a/ovn-nb.xml b/ovn-nb.xml
289index f30cc9ee9..a57e7498f 100644
290--- a/ovn-nb.xml
291+++ b/ovn-nb.xml
292@@ -3188,7 +3188,13 @@
293 Key-value pair of DNS records with <code>DNS query name</code> as the key
294 and value as a string of IP address(es) separated by comma or space.
295
296+ For PTR requests, the key-value pair can be
297+ <code>Reverse IPv4 address.in-addr.arpa</code> and the value
298+ <code>DNS domain name</code>. For IPv6 addresses, the key
299+ has to be <code>Reverse IPv6 address.ip6.arpa</code>.
300+
301 <p><b>Example: </b> "vm1.ovn.org" = "10.0.0.4 aef0::4"</p>
302+ <p><b>Example: </b> "4.0.0.10.in-addr.arpa" = "vm1.ovn.org"</p>
303 </column>
304
305 <column name="external_ids">
306diff --git a/tests/ovn.at b/tests/ovn.at
307index b6eff4349..87d0bd4fc 100644
308--- a/tests/ovn.at
309+++ b/tests/ovn.at
310@@ -8256,10 +8256,13 @@ ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
311
312 DNS1=`ovn-nbctl create DNS records={}`
313 DNS2=`ovn-nbctl create DNS records={}`
314+DNS3=`ovn-nbctl create DNS records={}`
315
316 ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
317 ovn-nbctl set DNS $DNS1 records:vm2.ovn.org="10.0.0.6 20.0.0.4"
318 ovn-nbctl set DNS $DNS2 records:vm3.ovn.org="40.0.0.4"
319+ovn-nbctl set DNS $DNS3 records:4.0.0.10.in-addr.arpa="vm1.ovn.org"
320+ovn-nbctl set DNS $DNS3 records:4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.e.a.ip6.arpa="vm1.ovn.org"
321
322 ovn-nbctl set Logical_switch ls1 dns_records="$DNS1"
323
324@@ -8365,6 +8368,21 @@ set_dns_params() {
325 vm1_incomplete)
326 # set type to none
327 type=''
328+ ;;
329+ vm1_ipv4_ptr)
330+ # 4.0.0.10.in-addr.arpa
331+ query_name=01340130013002313007696e2d61646472046172706100
332+ type=000c
333+ # vm1.ovn.org
334+ expected_dns_answer=${query_name}${type}0001${ttl}000d03766d31036f766e036f726700
335+ ;;
336+ vm1_ipv6_ptr)
337+ # 4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.e.a.ip6.arpa
338+ query_name=0134013001300130013001300130013001300130013001300130013001300130013001300130013001300130013001300130013001300130013001660165016103697036046172706100
339+ type=000c
340+ # vm1.ovn.org
341+ expected_dns_answer=${query_name}${type}0001${ttl}000d03766d31036f766e036f726700
342+ ;;
343 esac
344 # TTL - 3600
345 local dns_req_header=010201200001000000000000
346@@ -8464,6 +8482,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
347 rm -f 1.expected
348 rm -f 2.expected
349
350+
351 set_dns_params vm1
352 src_ip=`ip_to_hex 10 0 0 6`
353 dst_ip=`ip_to_hex 10 0 0 1`
354@@ -8485,8 +8504,8 @@ reset_pcap_file hv1-vif2 hv1/vif2
355 rm -f 1.expected
356 rm -f 2.expected
357
358-# Try vm1 again but an all-caps query name
359
360+# Try vm1 again but an all-caps query name
361 set_dns_params VM1
362 src_ip=`ip_to_hex 10 0 0 6`
363 dst_ip=`ip_to_hex 10 0 0 1`
364@@ -8508,6 +8527,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
365 rm -f 1.expected
366 rm -f 2.expected
367
368+
369 # Clear the query name options for ls1-lp2
370 ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
371
372@@ -8528,6 +8548,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
373 rm -f 1.expected
374 rm -f 2.expected
375
376+
377 # Clear the query name for ls1-lp1
378 # Since ls1 has no query names configued,
379 # ovn-northd should not add the DNS flows.
380@@ -8550,6 +8571,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
381 rm -f 1.expected
382 rm -f 2.expected
383
384+
385 # Test IPv6 (AAAA records) using IPv4 packet.
386 # Add back the DNS options for ls1-lp1.
387 ovn-nbctl --wait=hv set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
388@@ -8575,6 +8597,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
389 rm -f 1.expected
390 rm -f 2.expected
391
392+
393 # Test both IPv4 (A) and IPv6 (AAAA records) using IPv4 packet.
394 set_dns_params vm1_ipv4_v6
395 src_ip=`ip_to_hex 10 0 0 6`
396@@ -8597,6 +8620,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
397 rm -f 1.expected
398 rm -f 2.expected
399
400+
401 # Invalid type.
402 set_dns_params vm1_invalid_type
403 src_ip=`ip_to_hex 10 0 0 6`
404@@ -8615,6 +8639,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
405 rm -f 1.expected
406 rm -f 2.expected
407
408+
409 # Incomplete DNS packet.
410 set_dns_params vm1_incomplete
411 src_ip=`ip_to_hex 10 0 0 6`
412@@ -8633,6 +8658,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
413 rm -f 1.expected
414 rm -f 2.expected
415
416+
417 # Add one more DNS record to the ls1.
418 ovn-nbctl --wait=hv set Logical_switch ls1 dns_records="$DNS1 $DNS2"
419
420@@ -8657,6 +8683,7 @@ reset_pcap_file hv1-vif2 hv1/vif2
421 rm -f 1.expected
422 rm -f 2.expected
423
424+
425 # Try DNS query over IPv6
426 set_dns_params vm1
427 src_ip=aef00000000000000000000000000004
428@@ -8677,6 +8704,60 @@ reset_pcap_file hv1-vif2 hv1/vif2
429 rm -f 1.expected
430 rm -f 2.expected
431
432+
433+# Add one more DNS record to the ls1.
434+ovn-nbctl --wait=hv set Logical_switch ls1 dns_records="$DNS1 $DNS2 $DNS3"
435+echo "*************************"
436+ovn-sbctl list DNS
437+echo "*************************"
438+ovn-nbctl list DNS
439+echo "*************************"
440+
441+# Test PTR record for IPv4 address using IPv4 packet.
442+set_dns_params vm1_ipv4_ptr
443+src_ip=`ip_to_hex 10 0 0 4`
444+dst_ip=`ip_to_hex 10 0 0 1`
445+dns_reply=1
446+test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
447+
448+# NXT_RESUMEs should be 11.
449+OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
450+
451+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
452+cat 1.expected | cut -c -48 > expout
453+AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
454+# Skipping the IPv4 checksum.
455+cat 1.expected | cut -c 53- > expout
456+AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
457+
458+reset_pcap_file hv1-vif1 hv1/vif1
459+reset_pcap_file hv1-vif2 hv1/vif2
460+rm -f 1.expected
461+rm -f 2.expected
462+
463+
464+# Test PTR record for IPv6 address using IPv4 packet.
465+set_dns_params vm1_ipv6_ptr
466+src_ip=`ip_to_hex 10 0 0 4`
467+dst_ip=`ip_to_hex 10 0 0 1`
468+dns_reply=1
469+test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
470+
471+# NXT_RESUMEs should be 12.
472+OVS_WAIT_UNTIL([test 12 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
473+
474+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
475+cat 1.expected | cut -c -48 > expout
476+AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
477+# Skipping the IPv4 checksum.
478+cat 1.expected | cut -c 53- > expout
479+AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
480+
481+reset_pcap_file hv1-vif1 hv1/vif1
482+reset_pcap_file hv1-vif2 hv1/vif2
483+rm -f 1.expected
484+rm -f 2.expected
485+
486 OVN_CLEANUP([hv1])
487
488 AT_CLEANUP
489--
4902.33.1
491
diff --git a/debian/patches/series b/debian/patches/series
index fbe5db9..ba80c08 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -4,3 +4,5 @@ lp-1914988-tests-Use-ovn_start-in-tests-ovn-controller.at.patch
4lp-1914988-tests-Make-certificate-generation-extendable.patch4lp-1914988-tests-Make-certificate-generation-extendable.patch
5lp-1914988-tests-Test-with-SSL-and-RBAC-for-controller-by-defau.patch5lp-1914988-tests-Test-with-SSL-and-RBAC-for-controller-by-defau.patch
6lp-1943266-physical-do-not-forward-traffic-from-localport-to-a-.patch6lp-1943266-physical-do-not-forward-traffic-from-localport-to-a-.patch
7lp-1857026-0001-DNS-Make-DNS-lookups-case-insensitive.patch
8lp-1857026-0002-controller-Add-support-for-PTR-DNS-requests.patch

Subscribers

People subscribed via source and target branches