Skip to content

Commit

Permalink
set ether dst addr for dnat on logical switch
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangzujian committed Jun 17, 2022
1 parent 6172f6a commit 94b73d9
Showing 1 changed file with 173 additions and 12 deletions.
185 changes: 173 additions & 12 deletions northd/northd.c
Expand Up @@ -73,6 +73,8 @@ static struct eth_addr svc_monitor_mac_ea;
* Otherwise, it will avoid using it. The default is true. */
static bool use_ct_inv_match = true;

static bool ls_dnat_mod_dl_dst = false;

#define MAX_OVN_TAGS 4096

/* Pipeline stages. */
Expand Down Expand Up @@ -114,18 +116,19 @@ enum ovn_stage {
PIPELINE_STAGE(SWITCH, IN, QOS_METER, 11, "ls_in_qos_meter") \
PIPELINE_STAGE(SWITCH, IN, LB, 12, "ls_in_lb") \
PIPELINE_STAGE(SWITCH, IN, ACL_AFTER_LB, 13, "ls_in_acl_after_lb") \
PIPELINE_STAGE(SWITCH, IN, STATEFUL, 14, "ls_in_stateful") \
PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 15, "ls_in_pre_hairpin") \
PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 16, "ls_in_nat_hairpin") \
PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 17, "ls_in_hairpin") \
PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 18, "ls_in_arp_rsp") \
PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 19, "ls_in_dhcp_options") \
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 20, "ls_in_dhcp_response") \
PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 21, "ls_in_dns_lookup") \
PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 22, "ls_in_dns_response") \
PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 23, "ls_in_external_port") \
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 24, "ls_in_l2_lkup") \
PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 25, "ls_in_l2_unknown") \
PIPELINE_STAGE(SWITCH, IN, AFTER_LB, 14, "ls_in_after_lb") \
PIPELINE_STAGE(SWITCH, IN, STATEFUL, 15, "ls_in_stateful") \
PIPELINE_STAGE(SWITCH, IN, PRE_HAIRPIN, 16, "ls_in_pre_hairpin") \
PIPELINE_STAGE(SWITCH, IN, NAT_HAIRPIN, 17, "ls_in_nat_hairpin") \
PIPELINE_STAGE(SWITCH, IN, HAIRPIN, 18, "ls_in_hairpin") \
PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 19, "ls_in_arp_rsp") \
PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 20, "ls_in_dhcp_options") \
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 21, "ls_in_dhcp_response") \
PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 22, "ls_in_dns_lookup") \
PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 23, "ls_in_dns_response") \
PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 24, "ls_in_external_port") \
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 25, "ls_in_l2_lkup") \
PIPELINE_STAGE(SWITCH, IN, L2_UNKNOWN, 26, "ls_in_l2_unknown") \
\
/* Logical switch egress stages. */ \
PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
Expand Down Expand Up @@ -7017,6 +7020,153 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, bool ct_lb_mark,
}
}

static void
build_lswitch_dnat_mod_dl_dst_rules(struct ovn_port *op,
struct hmap *lflows,
const struct hmap *ports,
struct ds *actions,
struct ds *match)
{
if (!ls_dnat_mod_dl_dst) {
return;
}
if (!op->nbsp) {
return;
}
if (!strcmp(op->nbsp->type, "virtual") ||
!strcmp(op->nbsp->type, "localport")) {
return;
}
if (lsp_is_external(op->nbsp) || op->has_unknown) {
return;
}

if (lsp_is_router(op->nbsp)) {
struct ovn_port *peer = ovn_port_get_peer(ports, op);
if (!peer || !peer->nbrp) {
return;
}

char *network_s = NULL, *joined_networks = NULL;
struct svec networks;
svec_init(&networks);

if (peer->lrp_networks.n_ipv4_addrs) {
ovs_be32 lla;
inet_pton(AF_INET, "169.254.0.0", &lla);

for (int i = 0; i < peer->lrp_networks.n_ipv4_addrs; i++) {
struct ipv4_netaddr *addrs = &peer->lrp_networks.ipv4_addrs[i];
if (addrs->plen >= 16 &&
(addrs->addr & htonl(0xffff0000)) == lla) {
// skip link local address
continue;
}

network_s = xasprintf("%s/%u", addrs->network_s, addrs->plen);
svec_add(&networks, network_s);
free(network_s);
}

ds_clear(match);
if (svec_is_empty(&networks)) {
ds_put_format(match, "ip4");
} else {
joined_networks = svec_join(&networks, ", ", "");
svec_clear(&networks);
ds_put_format(match, "ip4.dst != {%s}", joined_networks);
free(joined_networks);
}

ds_clear(actions);
ds_put_format(actions, "next;");
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 60,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);

ds_clear(match);
ds_put_format(match, "ip4");
ds_clear(actions);
ds_put_format(actions, "lookup_arp_ip(\"%s\", ip4.dst); next;",
peer->key);
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 40,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);
}

if (peer->lrp_networks.n_ipv6_addrs) {
for (int i = 0; i < peer->lrp_networks.n_ipv6_addrs; i++) {
struct ipv6_netaddr *addrs = &peer->lrp_networks.ipv6_addrs[i];
if (addrs->plen >= 10 &&
(addrs->addr.s6_addr[0] & 0xff) == 0xfe &&
(addrs->addr.s6_addr[1] & 0xc0) == 0x80) {
// skip link local address
continue;
}

network_s = xasprintf("%s/%u", addrs->network_s, addrs->plen);
svec_add(&networks, network_s);
free(network_s);
}

ds_clear(match);
if (svec_is_empty(&networks)) {
ds_put_format(match, "ip6");
} else {
joined_networks = svec_join(&networks, ", ", "");
svec_clear(&networks);
ds_put_format(match, "ip6.dst != {%s}", joined_networks);
free(joined_networks);
}

ds_clear(actions);
ds_put_format(actions, "next;");
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 60,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);

ds_clear(match);
ds_put_format(match, "ip6");
ds_clear(actions);
ds_put_format(actions, "lookup_arp_ip(\"%s\", ip6.dst); next;",
peer->key);
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 40,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);
}

svec_destroy(&networks);
return;
}

if (op->n_lsp_addrs != 1 || !strlen(op->lsp_addrs[0].ea_s) ||
(!op->lsp_addrs[0].n_ipv4_addrs && !op->lsp_addrs[0].n_ipv6_addrs)) {
return;
}

for (size_t i = 0; i < op->lsp_addrs[0].n_ipv4_addrs; i++) {
ds_clear(match);
ds_put_format(match, "ip4.dst == %s",
op->lsp_addrs[0].ipv4_addrs[i].addr_s);
ds_clear(actions);
ds_put_format(actions, "eth.dst = %s; next;", op->lsp_addrs[0].ea_s);
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 50,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);
}

for (size_t i = 0; i < op->lsp_addrs[0].n_ipv6_addrs; i++) {
ds_clear(match);
ds_put_format(match, "ip6.dst == %s",
op->lsp_addrs[0].ipv6_addrs[i].addr_s);
ds_clear(actions);
ds_put_format(actions, "eth.dst = %s; next;", op->lsp_addrs[0].ea_s);
ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_AFTER_LB, 50,
ds_cstr(match), ds_cstr(actions),
&op->nbsp->header_);
}
}

static void
build_stateful(struct ovn_datapath *od,
const struct chassis_features *features,
Expand All @@ -7030,6 +7180,11 @@ build_stateful(struct ovn_datapath *od,
/* Ingress LB, Ingress and Egress stateful Table (Priority 0): Packets are
* allowed by default. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;");
if (ls_dnat_mod_dl_dst) {
ovn_lflow_add(lflows, od, S_SWITCH_IN_AFTER_LB, 100,
"!ct.dnat", "next;");
}
ovn_lflow_add(lflows, od, S_SWITCH_IN_AFTER_LB, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;");

Expand Down Expand Up @@ -13675,6 +13830,10 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
lsi->meter_groups,
&lsi->actions,
&lsi->match);
build_lswitch_dnat_mod_dl_dst_rules(op, lsi->lflows,
lsi->ports,
&lsi->actions,
&lsi->match);
build_lswitch_dhcp_options_and_response(op, lsi->lflows,
lsi->meter_groups);
build_lswitch_external_port(op, lsi->lflows);
Expand Down Expand Up @@ -15286,6 +15445,8 @@ ovnnb_db_run(struct northd_input *input_data,
"controller_event", false);
check_lsp_is_up = !smap_get_bool(&nb->options,
"ignore_lsp_down", true);
ls_dnat_mod_dl_dst = smap_get_bool(&nb->options,
"ls_dnat_mod_dl_dst", false);

build_chassis_features(input_data, &data->features);
build_datapaths(input_data, ovnsb_txn, &data->datapaths, &data->lr_list);
Expand Down

0 comments on commit 94b73d9

Please sign in to comment.