@@ -91,7 +91,6 @@ static bool use_ct_inv_match = true;
static bool default_acl_drop;
#define MAX_OVN_TAGS 4096
-
/* Due to various hard-coded priorities need to implement ACLs, the
* northbound database supports a smaller range of ACL priorities than
@@ -153,6 +152,8 @@ static bool default_acl_drop;
#define REGBIT_LOOKUP_NEIGHBOR_IP_RESULT "reg9[3]"
#define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
#define REGBIT_KNOWN_LB_SESSION "reg9[6]"
+#define REGBIT_DHCP_RELAY_REQ_CHK "reg9[7]"
+#define REGBIT_DHCP_RELAY_RESP_CHK "reg9[8]"
/* Register to store the eth address associated to a router port for packets
* received in S_ROUTER_IN_ADMISSION.
@@ -168,6 +169,7 @@ static bool default_acl_drop;
#define REG_NEXT_HOP_IPV6 "xxreg0"
#define REG_SRC_IPV4 "reg1"
#define REG_SRC_IPV6 "xxreg1"
+#define REG_DHCP_RELAY_DIP_IPV4 "reg2"
#define REG_ROUTE_TABLE_ID "reg7"
/* Register used to store backend ipv6 address
@@ -232,7 +234,7 @@ static bool default_acl_drop;
* | R1 | SRC_IPV4 for ARP-REQ | 0 | | R | |
* | | (>= IP_INPUT) | | | E | NEXT_HOP_IPV6 (>= DEFRAG ) |
* +-----+---------------------------+---+-----------------+ G | |
- * | R2 | UNUSED | X | | 0 | |
+ * | R2 REG_DHCP_RELAY_DIP_IPV4 | X | | 0 | |
* | | | R | | | |
* +-----+---------------------------+ E | UNUSED | | |
* | R3 | UNUSED | G | | | |
@@ -259,7 +261,9 @@ static bool default_acl_drop;
* | | EGRESS_LOOPBACK/ | G | UNUSED |
* | R9 | PKT_LARGER/ | 4 | |
* | | LOOKUP_NEIGHBOR_RESULT/ | | |
- * | | SKIP_LOOKUP_NEIGHBOR} | | |
+ * | | SKIP_LOOKUP_NEIGHBOR/ | | |
+ * | |REGBIT_DHCP_RELAY_REQ_CHK/ | | |
+ * | |REGBIT_DHCP_RELAY_RESP_CHK}| | |
* | | | | |
* | | REG_ORIG_TP_DPORT_ROUTER | | |
* | | | | |
@@ -8555,6 +8559,85 @@ build_dhcpv6_options_flows(struct ovn_port *op,
ds_destroy(&match);
}
+static const char *
+ls_dhcp_relay_port(const struct ovn_datapath *od)
+{
+ return smap_get(&od->nbs->other_config, "dhcp_relay_port");
+}
+
+static void
+build_lswitch_dhcp_relay_flows(struct ovn_port *op,
+ const struct hmap *ls_ports,
+ struct lflow_table *lflows)
+{
+ if (op->nbrp || !op->nbsp) {
+ return;
+ }
+
+ /* consider only ports attached to VMs */
+ if (strcmp(op->nbsp->type, "")) {
+ return;
+ }
+
+ if (!op->od || !op->od->n_router_ports ||
+ !op->od->nbs) {
+ return;
+ }
+
+ /* configure dhcp relay flows only when peer router has
+ * relay config enabled */
+ const char *dhcp_relay_port = ls_dhcp_relay_port(op->od);
+ if (!dhcp_relay_port) {
+ return;
+ }
+
+ struct ds match = DS_EMPTY_INITIALIZER;
+ struct ds action = DS_EMPTY_INITIALIZER;
+ struct ovn_port *sp = ovn_port_find(ls_ports, dhcp_relay_port);
+
+ if (!sp || !sp->nbsp || !sp->peer) {
+ return;
+ }
+
+ struct ovn_port *rp = sp->peer;
+ if (!rp || !rp->nbrp || !rp->nbrp->dhcp_relay || rp->peer != sp) {
+ return;
+ }
+
+ char *server_ip_str = NULL;
+ uint16_t port;
+ int addr_family;
+ struct in6_addr server_ip;
+ struct nbrec_dhcp_relay *dhcp_relay = rp->nbrp->dhcp_relay;
+
+ if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str,
+ &server_ip, &port, &addr_family)) {
+ return;
+ }
+
+ if (server_ip_str == NULL) {
+ return;
+ }
+
+ ds_put_format(
+ &match, "inport == %s && eth.src == %s && "
+ "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
+ "udp.src == 68 && udp.dst == 67",
+ op->json_key, op->lsp_addrs[0].ea_s);
+ ds_put_format(&action,
+ "eth.dst=%s;outport=%s;next;/* DHCP_RELAY_REQ */",
+ rp->lrp_networks.ea_s,sp->json_key);
+ ovn_lflow_add_with_hint__(lflows, op->od,
+ S_SWITCH_IN_L2_LKUP, 100,
+ ds_cstr(&match),
+ ds_cstr(&action),
+ op->key,
+ NULL,
+ &op->nbsp->header_,
+ op->lflow_ref);
+ free(server_ip_str);
+}
+
static void
build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
const struct ovn_port *port,
@@ -9156,6 +9239,13 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op,
return;
}
+ if (op->od && op->od->nbs
+ && ls_dhcp_relay_port(op->od)) {
+ /* Don't add the DHCP server flows if DHCP Relay is enabled on the
+ * logical switch. */
+ return;
+ }
+
bool is_external = lsp_is_external(op->nbsp);
if (is_external && (!op->od->n_localnet_ports ||
!op->nbsp->ha_chassis_group)) {
@@ -13611,6 +13701,165 @@ build_dhcpv6_reply_flows_for_lrouter_port(
}
}
+static void
+build_dhcp_relay_flows_for_lrouter_port(
+ struct ovn_port *op, struct lflow_table *lflows,
+ struct ds *match, struct lflow_ref *lflow_ref)
+{
+ if (!op->nbrp || !op->nbrp->dhcp_relay) {
+ return;
+
+ }
+
+ /* configure dhcp relay flows only when peer switch has
+ * relay config enabled */
+ struct ovn_port *sp = op->peer;
+ if (!sp || !sp->nbsp || sp->peer != op ||
+ !sp->od || !ls_dhcp_relay_port(sp->od)) {
+ return;
+ }
+
+ struct nbrec_dhcp_relay *dhcp_relay = op->nbrp->dhcp_relay;
+ if (!dhcp_relay->servers) {
+ return;
+ }
+
+ int addr_family;
+ /* currently not supporting custom port,
+ * dhcp server port is always set to 67 when installing flows */
+ uint16_t port;
+ char *server_ip_str = NULL;
+ struct in6_addr server_ip;
+
+ if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str,
+ &server_ip, &port, &addr_family)) {
+ return;
+ }
+
+ if (server_ip_str == NULL) {
+ return;
+ }
+
+ struct ds dhcp_action = DS_EMPTY_INITIALIZER;
+ ds_clear(match);
+ ds_put_format(
+ match, "inport == %s && "
+ "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
+ "ip.frag == 0 && udp.src == 68 && udp.dst == 67",
+ op->json_key);
+ ds_put_format(&dhcp_action,
+ REGBIT_DHCP_RELAY_REQ_CHK
+ " = dhcp_relay_req_chk(%s, %s);"
+ "next; /* DHCP_RELAY_REQ */",
+ op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
+
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "inport == %s && "
+ "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
+ "udp.src == 68 && udp.dst == 67 && "
+ REGBIT_DHCP_RELAY_REQ_CHK,
+ op->json_key);
+ ds_put_format(&dhcp_action,
+ "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_RELAY_REQ */",
+ op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
+
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 100,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "inport == %s && "
+ "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
+ "udp.src == 68 && udp.dst == 67 && "
+ REGBIT_DHCP_RELAY_REQ_CHK" == 0",
+ op->json_key);
+ ds_put_format(&dhcp_action,
+ "drop; /* DHCP_RELAY_REQ */");
+
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 1,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "ip4.src == %s && ip4.dst == %s && "
+ "ip.frag == 0 && udp.src == 67 && udp.dst == 67",
+ server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
+ ds_put_format(&dhcp_action, "next;/* DHCP_RELAY_RESP */");
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "ip4.src == %s && ip4.dst == %s && "
+ "udp.src == 67 && udp.dst == 67",
+ server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
+ ds_put_format(&dhcp_action,
+ REG_DHCP_RELAY_DIP_IPV4" = ip4.dst;"
+ REGBIT_DHCP_RELAY_RESP_CHK
+ " = dhcp_relay_resp_chk(%s, %s);next;/* DHCP_RELAY_RESP */",
+ op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
+
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK,
+ 100,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "ip4.src == %s && "
+ REG_DHCP_RELAY_DIP_IPV4" == %s && "
+ "udp.src == 67 && udp.dst == 67 && "
+ REGBIT_DHCP_RELAY_RESP_CHK,
+ server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
+ ds_put_format(&dhcp_action,
+ "ip4.src=%s;udp.dst=68;"
+ "outport=%s;output; /* DHCP_RELAY_RESP */",
+ op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key);
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP,
+ 100,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ ds_put_format(
+ match, "ip4.src == %s && "
+ REG_DHCP_RELAY_DIP_IPV4" == %s && "
+ "udp.src == 67 && udp.dst == 67 && "
+ REGBIT_DHCP_RELAY_RESP_CHK" == 0",
+ server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
+ ds_put_format(&dhcp_action,
+ "drop; /* DHCP_RELAY_RESP */");
+ ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP,
+ 1,
+ ds_cstr(match), ds_cstr(&dhcp_action),
+ &op->nbrp->header_, lflow_ref);
+ ds_clear(match);
+ ds_clear(&dhcp_action);
+
+ free(server_ip_str);
+}
+
static void
build_ipv6_input_flows_for_lrouter_port(
struct ovn_port *op, struct lflow_table *lflows,
@@ -14881,6 +15130,13 @@ static void build_lr_nat_defrag_and_lb_default_flows(
lflow_ref);
ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;",
lflow_ref);
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_REQ, 0, "1",
+ "next;", lflow_ref);
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, 0, "1",
+ "next;", lflow_ref);
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP, 0, "1",
+ "next;", lflow_ref);
+
/* Send the IPv6 NS packets to next table. When ovn-controller
* generates IPv6 NS (for the action - nd_ns{}), the injected
@@ -15640,6 +15896,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions);
build_lswitch_ip_unicast_lookup(op, lflows, actions,
match);
+ build_lswitch_dhcp_relay_flows(op, ls_ports, lflows);
/* Build Logical Router Flows. */
build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
@@ -15669,6 +15926,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
op->lflow_ref);
build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
op->lflow_ref);
+ build_dhcp_relay_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
+ op->lflow_ref);
build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows,
&lsi->match, &lsi->actions,
lsi->meter_groups,
@@ -437,24 +437,29 @@ enum ovn_stage {
PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \
PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \
PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \
- PIPELINE_STAGE(ROUTER, IN, UNSNAT, 4, "lr_in_unsnat") \
- PIPELINE_STAGE(ROUTER, IN, DEFRAG, 5, "lr_in_defrag") \
- PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 6, "lr_in_lb_aff_check") \
- PIPELINE_STAGE(ROUTER, IN, DNAT, 7, "lr_in_dnat") \
- PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 8, "lr_in_lb_aff_learn") \
- PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 9, "lr_in_ecmp_stateful") \
- PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 10, "lr_in_nd_ra_options") \
- PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 11, "lr_in_nd_ra_response") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 12, "lr_in_ip_routing_pre") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 13, "lr_in_ip_routing") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 14, "lr_in_ip_routing_ecmp") \
- PIPELINE_STAGE(ROUTER, IN, POLICY, 15, "lr_in_policy") \
- PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 16, "lr_in_policy_ecmp") \
- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 17, "lr_in_arp_resolve") \
- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 18, "lr_in_chk_pkt_len") \
- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 19, "lr_in_larger_pkts") \
- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 20, "lr_in_gw_redirect") \
- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 21, "lr_in_arp_request") \
+ PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_REQ, 4, "lr_in_dhcp_relay_req") \
+ PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \
+ PIPELINE_STAGE(ROUTER, IN, DEFRAG, 6, "lr_in_defrag") \
+ PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 7, "lr_in_lb_aff_check") \
+ PIPELINE_STAGE(ROUTER, IN, DNAT, 8, "lr_in_dnat") \
+ PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 9, "lr_in_lb_aff_learn") \
+ PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 10, "lr_in_ecmp_stateful") \
+ PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 11, "lr_in_nd_ra_options") \
+ PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 12, "lr_in_nd_ra_response") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 13, "lr_in_ip_routing_pre") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 14, "lr_in_ip_routing") \
+ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 15, "lr_in_ip_routing_ecmp") \
+ PIPELINE_STAGE(ROUTER, IN, POLICY, 16, "lr_in_policy") \
+ PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 17, "lr_in_policy_ecmp") \
+ PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 18, \
+ "lr_in_dhcp_relay_resp_chk") \
+ PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 19, \
+ "lr_in_dhcp_relay_resp") \
+ PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 20, "lr_in_arp_resolve") \
+ PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 21, "lr_in_chk_pkt_len") \
+ PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 22, "lr_in_larger_pkts") \
+ PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 23, "lr_in_gw_redirect") \
+ PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 24, "lr_in_arp_request") \
\
/* Logical router egress stages. */ \
PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0, \
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
"version": "7.3.0",
- "cksum": "3546526738 34483",
+ "cksum": "3858903371 35372",
"tables": {
"NB_Global": {
"columns": {
@@ -436,6 +436,11 @@
"ipv6_prefix": {"type": {"key": "string",
"min": 0,
"max": "unlimited"}},
+ "dhcp_relay": {"type": {"key": {"type": "uuid",
+ "refTable": "DHCP_Relay",
+ "refType": "strong"},
+ "min": 0,
+ "max": 1}},
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}},
@@ -534,6 +539,18 @@
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
"isRoot": true},
+ "DHCP_Relay": {
+ "columns": {
+ "name": {"type": "string"},
+ "servers": {"type": {"key": "string",
+ "min": 0,
+ "max": 1}},
+ "options": {"type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": true},
"Connection": {
"columns": {
"target": {"type": "string"},
@@ -703,6 +703,13 @@
</ul>
</column>
+ <column name="other_config" key="dhcp_relay_port">
+ If set to the name of logical switch port of type <code>router</code>
+ then, DHCP Relay is enabled for this logical switch provided the
+ corresponding <ref table="Logical_Router_Port"/> has DHCP Relay
+ configured.
+ </column>
+
<column name="other_config" key="mac_only" type='{"type": "boolean"}'>
Value used to request to assign L2 address only if neither subnet
nor ipv6_prefix are specified
@@ -3066,6 +3073,11 @@ or
port has all ingress and egress traffic dropped.
</column>
+ <column name="dhcp_relay">
+ This column is used to enabled DHCP Relay. Please refer
+ to <ref table="DHCP_Relay"/> table.
+ </column>
+
<group title="Distributed Gateway Ports">
<p>
Gateways, as documented under <code>Gateways</code> in the OVN
@@ -4379,6 +4391,33 @@ or
</group>
</table>
+ <table name="DHCP_Relay" title="DHCP Relay">
+ <p>
+ OVN implements native DHCPv4 relay support which caters to the common
+ use case of relaying the DHCP requests to external DHCP server.
+ </p>
+ <column name="name">
+ <p>
+ A name for the DHCP Relay.
+ </p>
+ </column>
+ <column name="servers">
+ <p>
+ The DHCPv4 server IP address.
+ </p>
+ </column>
+ <column name="options">
+ <p>
+ Future purpose.
+ </p>
+ </column>
+ <group title="Common Columns">
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </group>
+ </table>
+
<table name="Connection" title="OVSDB client connections.">
<p>
Configuration for a database connection to an Open vSwitch database