default: return "<unknown>";
}
}
+
+/* Returns the type of the datapath to which a flow with the given 'stage' may
+ * be added. */
+static enum ovn_datapath_type
+ovn_stage_to_datapath_type(enum ovn_stage stage)
+{
+ switch (stage) {
+#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
+ case S_##DP_TYPE##_##PIPELINE##_##STAGE: return DP_##DP_TYPE;
+ PIPELINE_STAGES
+#undef PIPELINE_STAGE
+ default: OVS_NOT_REACHED();
+ }
+}
\f
static void
usage(void)
struct ovs_list list; /* In list of similar records. */
- /* Logical router data (digested from nbr). */
- const struct ovn_port *gateway_port;
- ovs_be32 gateway;
-
/* Logical switch data. */
struct ovn_port **router_ports;
size_t n_router_ports;
}
}
+/* Returns 'od''s datapath type. */
+static enum ovn_datapath_type
+ovn_datapath_get_type(const struct ovn_datapath *od)
+{
+ return od->nbs ? DP_SWITCH : DP_ROUTER;
+}
+
static struct ovn_datapath *
ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid)
{
NULL, nbr, NULL);
ovs_list_push_back(nb_only, &od->list);
}
-
- od->gateway = 0;
- if (nbr->default_gw) {
- ovs_be32 ip;
- if (!ip_parse(nbr->default_gw, &ip) || !ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'gateway' %s", nbr->default_gw);
- } else {
- od->gateway = ip;
- }
- }
-
- /* Set the gateway port to NULL. If there is a gateway, it will get
- * filled in as we go through the ports later. */
- od->gateway_port = NULL;
}
}
char *key; /* nbs->name, nbr->name, sb->logical_port. */
char *json_key; /* 'key', quoted for use in JSON. */
- const struct nbrec_logical_switch_port *nbs; /* May be NULL. */
- const struct nbrec_logical_router_port *nbr; /* May be NULL. */
const struct sbrec_port_binding *sb; /* May be NULL. */
+ /* Logical switch port data. */
+ const struct nbrec_logical_switch_port *nbsp; /* May be NULL. */
+
+ struct lport_addresses *lsp_addrs; /* Logical switch port addresses. */
+ unsigned int n_lsp_addrs;
+
+ struct lport_addresses *ps_addrs; /* Port security addresses. */
+ unsigned int n_ps_addrs;
+
/* Logical router port data. */
- ovs_be32 ip, mask; /* 192.168.10.123/24. */
- ovs_be32 network; /* 192.168.10.0. */
- ovs_be32 bcast; /* 192.168.10.255. */
- struct eth_addr mac;
+ const struct nbrec_logical_router_port *nbrp; /* May be NULL. */
+
+ struct lport_addresses lrp_networks;
+
struct ovn_port *peer;
struct ovn_datapath *od;
static struct ovn_port *
ovn_port_create(struct hmap *ports, const char *key,
- const struct nbrec_logical_switch_port *nbs,
- const struct nbrec_logical_router_port *nbr,
+ const struct nbrec_logical_switch_port *nbsp,
+ const struct nbrec_logical_router_port *nbrp,
const struct sbrec_port_binding *sb)
{
struct ovn_port *op = xzalloc(sizeof *op);
op->key = xstrdup(key);
op->sb = sb;
- op->nbs = nbs;
- op->nbr = nbr;
+ op->nbsp = nbsp;
+ op->nbrp = nbrp;
hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
return op;
}
* private list and once we've exited that function it is not safe to
* use it. */
hmap_remove(ports, &port->key_node);
+
+ for (int i = 0; i < port->n_lsp_addrs; i++) {
+ destroy_lport_addresses(&port->lsp_addrs[i]);
+ }
+ free(port->lsp_addrs);
+
+ for (int i = 0; i < port->n_ps_addrs; i++) {
+ destroy_lport_addresses(&port->ps_addrs[i]);
+ }
+ free(port->ps_addrs);
+
+ destroy_lport_addresses(&port->lrp_networks);
free(port->json_key);
free(port->key);
free(port);
HMAP_FOR_EACH (od, key_node, datapaths) {
if (od->nbs) {
for (size_t i = 0; i < od->nbs->n_ports; i++) {
- const struct nbrec_logical_switch_port *nbs = od->nbs->ports[i];
- struct ovn_port *op = ovn_port_find(ports, nbs->name);
+ const struct nbrec_logical_switch_port *nbsp
+ = od->nbs->ports[i];
+ struct ovn_port *op = ovn_port_find(ports, nbsp->name);
if (op) {
- if (op->nbs || op->nbr) {
+ if (op->nbsp || op->nbrp) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "duplicate logical port %s",
- nbs->name);
+ nbsp->name);
continue;
}
- op->nbs = nbs;
+ op->nbsp = nbsp;
ovs_list_remove(&op->list);
ovs_list_push_back(both, &op->list);
+
+ /* This port exists due to a SB binding, but should
+ * not have been initialized fully. */
+ ovs_assert(!op->n_lsp_addrs && !op->n_ps_addrs);
} else {
- op = ovn_port_create(ports, nbs->name, nbs, NULL, NULL);
+ op = ovn_port_create(ports, nbsp->name, nbsp, NULL, NULL);
ovs_list_push_back(nb_only, &op->list);
}
+ op->lsp_addrs
+ = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
+ for (size_t j = 0; j < nbsp->n_addresses; j++) {
+ if (!strcmp(nbsp->addresses[j], "unknown")) {
+ continue;
+ }
+ if (!extract_lsp_addresses(nbsp->addresses[j],
+ &op->lsp_addrs[op->n_lsp_addrs])) {
+ static struct vlog_rate_limit rl
+ = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_INFO_RL(&rl, "invalid syntax '%s' in logical "
+ "switch port addresses. No MAC "
+ "address found",
+ op->nbsp->addresses[j]);
+ continue;
+ }
+ op->n_lsp_addrs++;
+ }
+
+ op->ps_addrs
+ = xmalloc(sizeof *op->ps_addrs * nbsp->n_port_security);
+ for (size_t j = 0; j < nbsp->n_port_security; j++) {
+ if (!extract_lsp_addresses(nbsp->port_security[j],
+ &op->ps_addrs[op->n_ps_addrs])) {
+ static struct vlog_rate_limit rl
+ = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_INFO_RL(&rl, "invalid syntax '%s' in port "
+ "security. No MAC address found",
+ op->nbsp->port_security[j]);
+ continue;
+ }
+ op->n_ps_addrs++;
+ }
+
op->od = od;
}
} else {
for (size_t i = 0; i < od->nbr->n_ports; i++) {
- const struct nbrec_logical_router_port *nbr
+ const struct nbrec_logical_router_port *nbrp
= od->nbr->ports[i];
- struct eth_addr mac;
- if (!eth_addr_from_string(nbr->mac, &mac)) {
+ struct lport_addresses lrp_networks;
+ if (!extract_lrp_networks(nbrp, &lrp_networks)) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'mac' %s", nbr->mac);
+ VLOG_WARN_RL(&rl, "bad 'mac' %s", nbrp->mac);
continue;
}
- ovs_be32 ip, mask;
- char *error = ip_parse_masked(nbr->network, &ip, &mask);
- if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'network' %s", nbr->network);
- free(error);
+ if (!lrp_networks.n_ipv4_addrs && !lrp_networks.n_ipv6_addrs) {
continue;
}
- struct ovn_port *op = ovn_port_find(ports, nbr->name);
+ struct ovn_port *op = ovn_port_find(ports, nbrp->name);
if (op) {
- if (op->nbs || op->nbr) {
+ if (op->nbsp || op->nbrp) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "duplicate logical router port %s",
- nbr->name);
+ nbrp->name);
continue;
}
- op->nbr = nbr;
+ op->nbrp = nbrp;
ovs_list_remove(&op->list);
ovs_list_push_back(both, &op->list);
+
+ /* This port exists but should not have been
+ * initialized fully. */
+ ovs_assert(!op->lrp_networks.n_ipv4_addrs
+ && !op->lrp_networks.n_ipv6_addrs);
} else {
- op = ovn_port_create(ports, nbr->name, NULL, nbr, NULL);
+ op = ovn_port_create(ports, nbrp->name, NULL, nbrp, NULL);
ovs_list_push_back(nb_only, &op->list);
}
- op->ip = ip;
- op->mask = mask;
- op->network = ip & mask;
- op->bcast = ip | ~mask;
- op->mac = mac;
-
+ op->lrp_networks = lrp_networks;
op->od = od;
-
- /* If 'od' has a gateway and 'op' routes to it... */
- if (od->gateway && !((op->network ^ od->gateway) & op->mask)) {
- /* ...and if 'op' is a longer match than the current
- * choice... */
- const struct ovn_port *gw = od->gateway_port;
- int len = gw ? ip_count_cidr_bits(gw->mask) : 0;
- if (ip_count_cidr_bits(op->mask) > len) {
- /* ...then it's the default gateway port. */
- od->gateway_port = op;
- }
- }
}
}
}
* to their peers. */
struct ovn_port *op;
HMAP_FOR_EACH (op, key_node, ports) {
- if (op->nbs && !strcmp(op->nbs->type, "router")) {
- const char *peer_name = smap_get(&op->nbs->options, "router-port");
+ if (op->nbsp && !strcmp(op->nbsp->type, "router")) {
+ const char *peer_name = smap_get(&op->nbsp->options, "router-port");
if (!peer_name) {
continue;
}
struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbr) {
+ if (!peer || !peer->nbrp) {
continue;
}
op->od->router_ports,
sizeof *op->od->router_ports * (op->od->n_router_ports + 1));
op->od->router_ports[op->od->n_router_ports++] = op;
- } else if (op->nbr && op->nbr->peer) {
- op->peer = ovn_port_find(ports, op->nbr->peer);
+ } else if (op->nbrp && op->nbrp->peer) {
+ op->peer = ovn_port_find(ports, op->nbrp->peer);
}
}
}
ovn_port_update_sbrec(const struct ovn_port *op)
{
sbrec_port_binding_set_datapath(op->sb, op->od->sb);
- if (op->nbr) {
+ if (op->nbrp) {
/* If the router is for l3 gateway, it resides on a chassis
* and its port type is "gateway". */
const char *chassis = smap_get(&op->od->nbr->options, "chassis");
sbrec_port_binding_set_tag(op->sb, NULL, 0);
sbrec_port_binding_set_mac(op->sb, NULL, 0);
} else {
- if (strcmp(op->nbs->type, "router")) {
- sbrec_port_binding_set_type(op->sb, op->nbs->type);
- sbrec_port_binding_set_options(op->sb, &op->nbs->options);
+ if (strcmp(op->nbsp->type, "router")) {
+ sbrec_port_binding_set_type(op->sb, op->nbsp->type);
+ sbrec_port_binding_set_options(op->sb, &op->nbsp->options);
} else {
const char *chassis = NULL;
if (op->peer && op->peer->od && op->peer->od->nbr) {
sbrec_port_binding_set_type(op->sb, "patch");
}
- const char *router_port = smap_get(&op->nbs->options,
+ const char *router_port = smap_get(&op->nbsp->options,
"router-port");
if (!router_port) {
router_port = "<error>";
sbrec_port_binding_set_options(op->sb, &new);
smap_destroy(&new);
}
- sbrec_port_binding_set_parent_port(op->sb, op->nbs->parent_name);
- sbrec_port_binding_set_tag(op->sb, op->nbs->tag, op->nbs->n_tag);
- sbrec_port_binding_set_mac(op->sb, (const char **) op->nbs->addresses,
- op->nbs->n_addresses);
+ sbrec_port_binding_set_parent_port(op->sb, op->nbsp->parent_name);
+ sbrec_port_binding_set_tag(op->sb, op->nbsp->tag, op->nbsp->n_tag);
+ sbrec_port_binding_set_mac(op->sb, (const char **) op->nbsp->addresses,
+ op->nbsp->n_addresses);
}
}
enum ovn_stage stage, uint16_t priority,
const char *match, const char *actions)
{
+ ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
+
struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
ovn_lflow_init(lflow, od, stage, priority,
xstrdup(match), xstrdup(actions));
}
/* Appends port security constraints on L2 address field 'eth_addr_field'
- * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with
- * 'n_port_security' elements, is the collection of port_security constraints
- * from an OVN_NB Logical_Switch_Port row. */
+ * (e.g. "eth.src" or "eth.dst") to 'match'. 'ps_addrs', with 'n_ps_addrs'
+ * elements, is the collection of port_security constraints from an
+ * OVN_NB Logical_Switch_Port row generated by extract_lsp_addresses(). */
static void
build_port_security_l2(const char *eth_addr_field,
- char **port_security, size_t n_port_security,
+ struct lport_addresses *ps_addrs,
+ unsigned int n_ps_addrs,
struct ds *match)
{
- size_t base_len = match->length;
- ds_put_format(match, " && %s == {", eth_addr_field);
+ if (!n_ps_addrs) {
+ return;
+ }
- size_t n = 0;
- for (size_t i = 0; i < n_port_security; i++) {
- struct eth_addr ea;
+ ds_put_format(match, " && %s == {", eth_addr_field);
- if (eth_addr_from_string(port_security[i], &ea)) {
- ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(ea));
- ds_put_char(match, ' ');
- n++;
- }
+ for (size_t i = 0; i < n_ps_addrs; i++) {
+ ds_put_format(match, "%s ", ps_addrs[i].ea_s);
}
ds_chomp(match, ' ');
ds_put_cstr(match, "}");
-
- if (!n) {
- match->length = base_len;
- }
}
static void
static void
build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
{
- for (size_t i = 0; i < op->nbs->n_port_security; i++) {
- struct lport_addresses ps;
- if (!extract_lsp_addresses(op->nbs->port_security[i], &ps, true)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "invalid syntax '%s' in port security. No MAC"
- " address found", op->nbs->port_security[i]);
- continue;
- }
+ struct ds match = DS_EMPTY_INITIALIZER;
- bool no_ip = !(ps.n_ipv4_addrs || ps.n_ipv6_addrs);
- struct ds match = DS_EMPTY_INITIALIZER;
+ for (size_t i = 0; i < op->n_ps_addrs; i++) {
+ struct lport_addresses *ps = &op->ps_addrs[i];
- if (ps.n_ipv4_addrs || no_ip) {
- ds_put_format(
- &match, "inport == %s && eth.src == "ETH_ADDR_FMT" && arp.sha == "
- ETH_ADDR_FMT, op->json_key, ETH_ADDR_ARGS(ps.ea),
- ETH_ADDR_ARGS(ps.ea));
+ bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs);
- if (ps.n_ipv4_addrs) {
- ds_put_cstr(&match, " && (");
- for (size_t i = 0; i < ps.n_ipv4_addrs; i++) {
- ds_put_cstr(&match, "arp.spa == ");
- ovs_be32 mask = be32_prefix_mask(ps.ipv4_addrs[i].plen);
+ ds_clear(&match);
+ if (ps->n_ipv4_addrs || no_ip) {
+ ds_put_format(&match,
+ "inport == %s && eth.src == %s && arp.sha == %s",
+ op->json_key, ps->ea_s, ps->ea_s);
+
+ if (ps->n_ipv4_addrs) {
+ ds_put_cstr(&match, " && arp.spa == {");
+ for (size_t j = 0; j < ps->n_ipv4_addrs; j++) {
/* When the netmask is applied, if the host portion is
* non-zero, the host can only use the specified
* address in the arp.spa. If zero, the host is allowed
* to use any address in the subnet. */
- if (ps.ipv4_addrs[i].addr & ~mask) {
- ds_put_format(&match, IP_FMT,
- IP_ARGS(ps.ipv4_addrs[i].addr));
+ if (ps->ipv4_addrs[j].plen == 32
+ || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) {
+ ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s);
} else {
- ip_format_masked(ps.ipv4_addrs[i].addr & mask, mask,
- &match);
+ ds_put_format(&match, "%s/%d",
+ ps->ipv4_addrs[j].network_s,
+ ps->ipv4_addrs[j].plen);
}
- ds_put_cstr(&match, " || ");
+ ds_put_cstr(&match, ", ");
}
ds_chomp(&match, ' ');
- ds_chomp(&match, '|');
- ds_chomp(&match, '|');
- ds_put_cstr(&match, ")");
+ ds_chomp(&match, ',');
+ ds_put_cstr(&match, "}");
}
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
ds_cstr(&match), "next;");
- ds_destroy(&match);
}
- if (ps.n_ipv6_addrs || no_ip) {
- ds_init(&match);
- ds_put_format(&match, "inport == %s && eth.src == "ETH_ADDR_FMT,
- op->json_key, ETH_ADDR_ARGS(ps.ea));
- build_port_security_ipv6_nd_flow(&match, ps.ea, ps.ipv6_addrs,
- ps.n_ipv6_addrs);
+ if (ps->n_ipv6_addrs || no_ip) {
+ ds_clear(&match);
+ ds_put_format(&match, "inport == %s && eth.src == %s",
+ op->json_key, ps->ea_s);
+ build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
+ ps->n_ipv6_addrs);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
ds_cstr(&match), "next;");
- ds_destroy(&match);
}
- free(ps.ipv4_addrs);
- free(ps.ipv6_addrs);
}
- char *match = xasprintf("inport == %s && (arp || nd)", op->json_key);
+ ds_clear(&match);
+ ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
- match, "drop;");
- free(match);
+ ds_cstr(&match), "drop;");
+ ds_destroy(&match);
}
/**
stage = S_SWITCH_OUT_PORT_SEC_IP;
}
- for (size_t i = 0; i < op->nbs->n_port_security; i++) {
- struct lport_addresses ps;
- if (!extract_lsp_addresses(op->nbs->port_security[i], &ps, true)) {
- continue;
- }
+ for (size_t i = 0; i < op->n_ps_addrs; i++) {
+ struct lport_addresses *ps = &op->ps_addrs[i];
- if (!(ps.n_ipv4_addrs || ps.n_ipv6_addrs)) {
+ if (!(ps->n_ipv4_addrs || ps->n_ipv6_addrs)) {
continue;
}
- if (ps.n_ipv4_addrs) {
+ if (ps->n_ipv4_addrs) {
struct ds match = DS_EMPTY_INITIALIZER;
if (pipeline == P_IN) {
/* Permit use of the unspecified address for DHCP discovery */
struct ds dhcp_match = DS_EMPTY_INITIALIZER;
ds_put_format(&dhcp_match, "inport == %s"
- " && eth.src == "ETH_ADDR_FMT
+ " && eth.src == %s"
" && ip4.src == 0.0.0.0"
" && ip4.dst == 255.255.255.255"
- " && udp.src == 68 && udp.dst == 67", op->json_key,
- ETH_ADDR_ARGS(ps.ea));
+ " && udp.src == 68 && udp.dst == 67",
+ op->json_key, ps->ea_s);
ovn_lflow_add(lflows, op->od, stage, 90,
ds_cstr(&dhcp_match), "next;");
ds_destroy(&dhcp_match);
- ds_put_format(&match, "inport == %s && eth.src == "ETH_ADDR_FMT
+ ds_put_format(&match, "inport == %s && eth.src == %s"
" && ip4.src == {", op->json_key,
- ETH_ADDR_ARGS(ps.ea));
+ ps->ea_s);
} else {
- ds_put_format(&match, "outport == %s && eth.dst == "ETH_ADDR_FMT
+ ds_put_format(&match, "outport == %s && eth.dst == %s"
" && ip4.dst == {255.255.255.255, 224.0.0.0/4, ",
- op->json_key, ETH_ADDR_ARGS(ps.ea));
+ op->json_key, ps->ea_s);
}
- for (int i = 0; i < ps.n_ipv4_addrs; i++) {
- ovs_be32 mask = be32_prefix_mask(ps.ipv4_addrs[i].plen);
+ for (int j = 0; j < ps->n_ipv4_addrs; j++) {
+ ovs_be32 mask = ps->ipv4_addrs[j].mask;
/* When the netmask is applied, if the host portion is
* non-zero, the host can only use the specified
* address. If zero, the host is allowed to use any
* address in the subnet.
- * */
- if (ps.ipv4_addrs[i].addr & ~mask) {
- ds_put_format(&match, IP_FMT,
- IP_ARGS(ps.ipv4_addrs[i].addr));
- if (pipeline == P_OUT && ps.ipv4_addrs[i].plen != 32) {
- /* Host is also allowed to receive packets to the
- * broadcast address in the specified subnet.
- */
- ds_put_format(&match, ", "IP_FMT,
- IP_ARGS(ps.ipv4_addrs[i].addr | ~mask));
+ */
+ if (ps->ipv4_addrs[j].plen == 32
+ || ps->ipv4_addrs[j].addr & ~mask) {
+ ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s);
+ if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) {
+ /* Host is also allowed to receive packets to the
+ * broadcast address in the specified subnet. */
+ ds_put_format(&match, ", %s",
+ ps->ipv4_addrs[j].bcast_s);
}
} else {
/* host portion is zero */
- ip_format_masked(ps.ipv4_addrs[i].addr & mask, mask,
- &match);
+ ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s,
+ ps->ipv4_addrs[j].plen);
}
ds_put_cstr(&match, ", ");
}
ds_put_cstr(&match, "}");
ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match), "next;");
ds_destroy(&match);
- free(ps.ipv4_addrs);
}
- if (ps.n_ipv6_addrs) {
+ if (ps->n_ipv6_addrs) {
struct ds match = DS_EMPTY_INITIALIZER;
if (pipeline == P_IN) {
/* Permit use of unspecified address for duplicate address
* detection */
struct ds dad_match = DS_EMPTY_INITIALIZER;
ds_put_format(&dad_match, "inport == %s"
- " && eth.src == "ETH_ADDR_FMT
+ " && eth.src == %s"
" && ip6.src == ::"
" && ip6.dst == ff02::/16"
" && icmp6.type == {131, 135, 143}", op->json_key,
- ETH_ADDR_ARGS(ps.ea));
+ ps->ea_s);
ovn_lflow_add(lflows, op->od, stage, 90,
ds_cstr(&dad_match), "next;");
ds_destroy(&dad_match);
}
- ds_put_format(&match, "%s == %s && %s == "ETH_ADDR_FMT"",
+ ds_put_format(&match, "%s == %s && %s == %s",
port_direction, op->json_key,
- pipeline == P_IN ? "eth.src" : "eth.dst",
- ETH_ADDR_ARGS(ps.ea));
- build_port_security_ipv6_flow(pipeline, &match, ps.ea,
- ps.ipv6_addrs, ps.n_ipv6_addrs);
+ pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
+ build_port_security_ipv6_flow(pipeline, &match, ps->ea,
+ ps->ipv6_addrs, ps->n_ipv6_addrs);
ovn_lflow_add(lflows, op->od, stage, 90,
ds_cstr(&match), "next;");
ds_destroy(&match);
- free(ps.ipv6_addrs);
}
- char *match = xasprintf(
- "%s == %s && %s == "ETH_ADDR_FMT" && ip", port_direction,
- op->json_key, pipeline == P_IN ? "eth.src" : "eth.dst",
- ETH_ADDR_ARGS(ps.ea));
+ char *match = xasprintf("%s == %s && %s == %s && ip",
+ port_direction, op->json_key,
+ pipeline == P_IN ? "eth.src" : "eth.dst",
+ ps->ea_s);
ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
free(match);
}
+
}
static bool
* defragmentation, in order to match L4 headers. */
if (has_stateful) {
HMAP_FOR_EACH (op, key_node, ports) {
- if (op->od == od && !strcmp(op->nbs->type, "router")) {
+ if (op->od == od && !strcmp(op->nbsp->type, "router")) {
/* Can't use ct() for router ports. Consider the
* following configuration: lp1(10.0.0.2) on
* hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
* commit IP flows. This is because, while the initiater's
* direction may not have any stateful rules, the server's may
* and then its return traffic would not have an associated
- * conntrack entry and would return "+invalid". */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1, "ip",
- REGBIT_CONNTRACK_COMMIT" = 1; next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1, "ip",
- REGBIT_CONNTRACK_COMMIT" = 1; next;");
+ * conntrack entry and would return "+invalid".
+ *
+ * We use "ct_commit" for a connection that is not already known
+ * by the connection tracker. Once a connection is committed,
+ * subsequent packets will hit the flow at priority 0 that just
+ * uses "next;"
+ *
+ * We also check for established connections that have ct_label[0]
+ * set on them. That's a connection that was disallowed, but is
+ * now allowed by policy again since it hit this default-allow flow.
+ * We need to set ct_label[0]=0 to let the connection continue,
+ * which will be done by ct_commit() in the "stateful" stage.
+ * Subsequent packets will hit the flow at priority 0 that just
+ * uses "next;". */
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1,
+ "ip && (!ct.est || (ct.est && ct_label[0] == 1))",
+ REGBIT_CONNTRACK_COMMIT" = 1; next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1,
+ "ip && (!ct.est || (ct.est && ct_label[0] == 1))",
+ REGBIT_CONNTRACK_COMMIT" = 1; next;");
/* Ingress and Egress ACL Table (Priority 65535).
*
- * Always drop traffic that's in an invalid state. This is
- * enforced at a higher priority than ACLs can be defined. */
+ * Always drop traffic that's in an invalid state. Also drop
+ * reply direction packets for connections that have been marked
+ * for deletion (bit 0 of ct_label is set).
+ *
+ * This is enforced at a higher priority than ACLs can be defined. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "ct.inv", "drop;");
+ "ct.inv || (ct.est && ct.rpl && ct_label[0] == 1)",
+ "drop;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "ct.inv", "drop;");
+ "ct.inv || (ct.est && ct.rpl && ct_label[0] == 1)",
+ "drop;");
/* Ingress and Egress ACL Table (Priority 65535).
*
- * Always allow traffic that is established to a committed
- * conntrack entry. This is enforced at a higher priority than
- * ACLs can be defined. */
+ * Allow reply traffic that is part of an established
+ * conntrack entry that has not been marked for deletion
+ * (bit 0 of ct_label). We only match traffic in the
+ * reply direction because we want traffic in the request
+ * direction to hit the currently defined policy from ACLs.
+ *
+ * This is enforced at a higher priority than ACLs can be defined. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv",
+ "ct.est && !ct.rel && !ct.new && !ct.inv "
+ "&& ct.rpl && ct_label[0] == 0",
"next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv",
+ "ct.est && !ct.rel && !ct.new && !ct.inv "
+ "&& ct.rpl && ct_label[0] == 0",
"next;");
/* Ingress and Egress ACL Table (Priority 65535).
*
- * Always allow traffic that is related to an existing conntrack
- * entry. This is enforced at a higher priority than ACLs can
- * be defined.
+ * Allow traffic that is related to an existing conntrack entry that
+ * has not been marked for deletion (bit 0 of ct_label).
+ *
+ * This is enforced at a higher priority than ACLs can be defined.
*
* NOTE: This does not support related data sessions (eg,
* a dynamically negotiated FTP data channel), but will allow
* related traffic such as an ICMP Port Unreachable through
* that's generated from a non-listening UDP port. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "!ct.est && ct.rel && !ct.new && !ct.inv",
+ "!ct.est && ct.rel && !ct.new && !ct.inv "
+ "&& ct_label[0] == 0",
"next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "!ct.est && ct.rel && !ct.new && !ct.inv",
+ "!ct.est && ct.rel && !ct.new && !ct.inv "
+ "&& ct_label[0] == 0",
"next;");
/* Ingress and Egress ACL Table (Priority 65535).
bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
- if (!strcmp(acl->action, "allow")) {
+ if (!strcmp(acl->action, "allow")
+ || !strcmp(acl->action, "allow-related")) {
/* If there are any stateful flows, we must even commit "allow"
* actions. This is because, while the initiater's
* direction may not have any stateful rules, the server's
* may and then its return traffic would not have an
* associated conntrack entry and would return "+invalid". */
- const char *actions = has_stateful
- ? REGBIT_CONNTRACK_COMMIT" = 1; next;"
- : "next;";
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- acl->match, actions);
- } else if (!strcmp(acl->action, "allow-related")) {
+ if (!has_stateful) {
+ ovn_lflow_add(lflows, od, stage,
+ acl->priority + OVN_ACL_PRI_OFFSET,
+ acl->match, "next;");
+ } else {
+ struct ds match = DS_EMPTY_INITIALIZER;
+
+ /* Commit the connection tracking entry if it's a new
+ * connection that matches this ACL. After this commit,
+ * the reply traffic is allowed by a flow we create at
+ * priority 65535, defined earlier.
+ *
+ * It's also possible that a known connection was marked for
+ * deletion after a policy was deleted, but the policy was
+ * re-added while that connection is still known. We catch
+ * that case here and un-set ct_label[0] (which will be done
+ * by ct_commit in the "stateful" stage) to indicate that the
+ * connection should be allowed to resume.
+ */
+ ds_put_format(&match, "((ct.new && !ct.est)"
+ " || (!ct.new && ct.est && !ct.rpl "
+ "&& ct_label[0] == 1)) "
+ "&& (%s)", acl->match);
+ ovn_lflow_add(lflows, od, stage,
+ acl->priority + OVN_ACL_PRI_OFFSET,
+ ds_cstr(&match),
+ REGBIT_CONNTRACK_COMMIT" = 1; next;");
+
+ /* Match on traffic in the request direction for an established
+ * connection tracking entry that has not been marked for
+ * deletion. There is no need to commit here, so we can just
+ * proceed to the next table. We use this to ensure that this
+ * connection is still allowed by the currently defined
+ * policy. */
+ ds_clear(&match);
+ ds_put_format(&match,
+ "!ct.new && ct.est && !ct.rpl"
+ " && ct_label[0] == 0 && (%s)",
+ acl->match);
+ ovn_lflow_add(lflows, od, stage,
+ acl->priority + OVN_ACL_PRI_OFFSET,
+ ds_cstr(&match), "next;");
+
+ ds_destroy(&match);
+ }
+ } else if (!strcmp(acl->action, "drop")
+ || !strcmp(acl->action, "reject")) {
struct ds match = DS_EMPTY_INITIALIZER;
- /* Commit the connection tracking entry, which allows all
- * other traffic related to this entry to flow due to the
- * 65535 priority flow defined earlier. */
- ds_put_format(&match, "ct.new && (%s)", acl->match);
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match),
- REGBIT_CONNTRACK_COMMIT" = 1; next;");
+ /* XXX Need to support "reject", treat it as "drop;" for now. */
+ if (!strcmp(acl->action, "reject")) {
+ VLOG_INFO("reject is not a supported action");
+ }
- ds_destroy(&match);
- } else if (!strcmp(acl->action, "drop")) {
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- acl->match, "drop;");
- } else if (!strcmp(acl->action, "reject")) {
- /* xxx Need to support "reject". */
- VLOG_INFO("reject is not a supported action");
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- acl->match, "drop;");
+ /* The implementation of "drop" differs if stateful ACLs are in
+ * use for this datapath. In that case, the actions differ
+ * depending on whether the connection was previously committed
+ * to the connection tracker with ct_commit. */
+ if (has_stateful) {
+ /* If the packet is not part of an established connection, then
+ * we can simply drop it. */
+ ds_put_format(&match,
+ "(!ct.est || (ct.est && ct_label[0] == 1)) "
+ "&& (%s)",
+ acl->match);
+ ovn_lflow_add(lflows, od, stage, acl->priority +
+ OVN_ACL_PRI_OFFSET, ds_cstr(&match), "drop;");
+
+ /* For an existing connection without ct_label set, we've
+ * encountered a policy change. ACLs previously allowed
+ * this connection and we committed the connection tracking
+ * entry. Current policy says that we should drop this
+ * connection. First, we set bit 0 of ct_label to indicate
+ * that this connection is set for deletion. By not
+ * specifying "next;", we implicitly drop the packet after
+ * updating conntrack state. We would normally defer
+ * ct_commit() to the "stateful" stage, but since we're
+ * dropping the packet, we go ahead and do it here. */
+ ds_clear(&match);
+ ds_put_format(&match,
+ "ct.est && ct_label[0] == 0 && (%s)",
+ acl->match);
+ ovn_lflow_add(lflows, od, stage,
+ acl->priority + OVN_ACL_PRI_OFFSET,
+ ds_cstr(&match), "ct_commit(ct_label=1/1);");
+
+ ds_destroy(&match);
+ } else {
+ /* There are no stateful ACLs in use on this datapath,
+ * so a "drop" ACL is simply the "drop" logical flow action
+ * in all cases. */
+ ovn_lflow_add(lflows, od, stage,
+ acl->priority + OVN_ACL_PRI_OFFSET,
+ acl->match, "drop;");
+ }
}
}
}
ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;");
/* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
- * committed to conntrack. */
+ * committed to conntrack. We always set ct_label[0] to 0 here as
+ * any packet that makes it this far is part of a connection we
+ * want to allow to continue. */
ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit; next;");
+ REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit; next;");
+ REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
/* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
* through nat (without committing).
/* This flow table structure is documented in ovn-northd(8), so please
* update ovn-northd.8.xml if you change anything. */
+ struct ds match = DS_EMPTY_INITIALIZER;
+ struct ds actions = DS_EMPTY_INITIALIZER;
+
/* Build pre-ACL and ACL tables for both ingress and egress.
* Ingress tables 3 and 4. Egress tables 0 and 1. */
struct ovn_datapath *od;
*/
struct ovn_port *op;
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
- if (!lsp_is_enabled(op->nbs)) {
+ if (!lsp_is_enabled(op->nbsp)) {
/* Drop packets from disabled logical ports (since logical flow
* tables are default-drop). */
continue;
}
- struct ds match = DS_EMPTY_INITIALIZER;
+ ds_clear(&match);
ds_put_format(&match, "inport == %s", op->json_key);
- build_port_security_l2(
- "eth.src", op->nbs->port_security, op->nbs->n_port_security,
- &match);
+ build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs,
+ &match);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
ds_cstr(&match), "next;");
- ds_destroy(&match);
- if (op->nbs->n_port_security) {
+ if (op->nbsp->n_port_security) {
build_port_security_ip(P_IN, op, lflows);
build_port_security_nd(op, lflows);
}
/* Ingress table 9: ARP responder, skip requests coming from localnet ports.
* (priority 100). */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
- if (!strcmp(op->nbs->type, "localnet")) {
- char *match = xasprintf("inport == %s", op->json_key);
+ if (!strcmp(op->nbsp->type, "localnet")) {
+ ds_clear(&match);
+ ds_put_format(&match, "inport == %s", op->json_key);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
- match, "next;");
- free(match);
+ ds_cstr(&match), "next;");
}
}
/* Ingress table 9: ARP/ND responder, reply for known IPs.
* (priority 50). */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
* - port is up or
* - port type is router
*/
- if (!lsp_is_up(op->nbs) && strcmp(op->nbs->type, "router")) {
+ if (!lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router")) {
continue;
}
- for (size_t i = 0; i < op->nbs->n_addresses; i++) {
- struct lport_addresses laddrs;
- if (!extract_lsp_addresses(op->nbs->addresses[i], &laddrs,
- true)) {
- continue;
- }
- for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
- char *match = xasprintf(
- "arp.tpa == "IP_FMT" && arp.op == 1",
- IP_ARGS(laddrs.ipv4_addrs[j].addr));
- char *actions = xasprintf(
+ for (size_t i = 0; i < op->n_lsp_addrs; i++) {
+ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
+ ds_clear(&match);
+ ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
+ op->lsp_addrs[i].ipv4_addrs[j].addr_s);
+ ds_clear(&actions);
+ ds_put_format(&actions,
"eth.dst = eth.src; "
- "eth.src = "ETH_ADDR_FMT"; "
+ "eth.src = %s; "
"arp.op = 2; /* ARP reply */ "
"arp.tha = arp.sha; "
- "arp.sha = "ETH_ADDR_FMT"; "
+ "arp.sha = %s; "
"arp.tpa = arp.spa; "
- "arp.spa = "IP_FMT"; "
+ "arp.spa = %s; "
"outport = inport; "
"inport = \"\"; /* Allow sending out inport. */ "
"output;",
- ETH_ADDR_ARGS(laddrs.ea),
- ETH_ADDR_ARGS(laddrs.ea),
- IP_ARGS(laddrs.ipv4_addrs[j].addr));
+ op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
+ op->lsp_addrs[i].ipv4_addrs[j].addr_s);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
- match, actions);
- free(match);
- free(actions);
+ ds_cstr(&match), ds_cstr(&actions));
}
- if (laddrs.n_ipv6_addrs > 0) {
- char ip6_str[INET6_ADDRSTRLEN + 1];
- struct ds match = DS_EMPTY_INITIALIZER;
+ if (op->lsp_addrs[i].n_ipv6_addrs > 0) {
+ ds_clear(&match);
ds_put_cstr(&match, "icmp6 && icmp6.type == 135 && ");
- if (laddrs.n_ipv6_addrs == 1) {
- ipv6_string_mapped(ip6_str,
- &(laddrs.ipv6_addrs[0].addr));
- ds_put_format(&match, "nd.target == %s", ip6_str);
+ if (op->lsp_addrs[i].n_ipv6_addrs == 1) {
+ ds_put_format(&match, "nd.target == %s",
+ op->lsp_addrs[i].ipv6_addrs[0].addr_s);
} else {
- ds_put_cstr(&match, "(");
- for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) {
- ipv6_string_mapped(ip6_str,
- &(laddrs.ipv6_addrs[j].addr));
- ds_put_format(&match, "nd.target == %s || ", ip6_str);
+ ds_put_format(&match, "nd.target == {");
+ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
+ ds_put_cstr(&match,
+ op->lsp_addrs[i].ipv6_addrs[j].addr_s);
}
ds_chomp(&match, ' ');
- ds_chomp(&match, '|');
- ds_chomp(&match, '|');
- ds_chomp(&match, ' ');
- ds_put_cstr(&match, ")");
+ ds_chomp(&match, ',');
+ ds_put_cstr(&match, "}");
}
- char *actions = xasprintf(
- "na { eth.src = "ETH_ADDR_FMT"; "
- "nd.tll = "ETH_ADDR_FMT"; "
+ ds_clear(&actions);
+ ds_put_format(&actions,
+ "na { eth.src = %s; "
+ "nd.tll = %s; "
"outport = inport; "
"inport = \"\"; /* Allow sending out inport. */ "
"output; };",
- ETH_ADDR_ARGS(laddrs.ea),
- ETH_ADDR_ARGS(laddrs.ea));
+ op->lsp_addrs[i].ea_s,
+ op->lsp_addrs[i].ea_s);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
- ds_cstr(&match), actions);
+ ds_cstr(&match), ds_cstr(&actions));
- ds_destroy(&match);
}
-
- free(laddrs.ipv4_addrs);
- free(laddrs.ipv6_addrs);
}
}
/* Ingress table 10: Destination lookup, broadcast and multicast handling
* (priority 100). */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
- if (lsp_is_enabled(op->nbs)) {
+ if (lsp_is_enabled(op->nbsp)) {
ovn_multicast_add(mcgroups, &mc_flood, op);
}
}
/* Ingress table 10: Destination lookup, unicast handling (priority 50), */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
- for (size_t i = 0; i < op->nbs->n_addresses; i++) {
+ for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
struct eth_addr mac;
- if (eth_addr_from_string(op->nbs->addresses[i], &mac)) {
- struct ds match, actions;
-
- ds_init(&match);
+ if (eth_addr_from_string(op->nbsp->addresses[i], &mac)) {
+ ds_clear(&match);
ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
ETH_ADDR_ARGS(mac));
- ds_init(&actions);
+ ds_clear(&actions);
ds_put_format(&actions, "outport = %s; output;", op->json_key);
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
ds_cstr(&match), ds_cstr(&actions));
- ds_destroy(&actions);
- ds_destroy(&match);
- } else if (!strcmp(op->nbs->addresses[i], "unknown")) {
- if (lsp_is_enabled(op->nbs)) {
+ } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
+ if (lsp_is_enabled(op->nbsp)) {
ovn_multicast_add(mcgroups, &mc_unknown, op);
op->od->has_unknown = true;
}
VLOG_INFO_RL(&rl,
"%s: invalid syntax '%s' in addresses column",
- op->nbs->name, op->nbs->addresses[i]);
+ op->nbsp->name, op->nbsp->addresses[i]);
}
}
}
* Priority 150 rules drop packets to disabled logical ports, so that they
* don't even receive multicast or broadcast packets. */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbs) {
+ if (!op->nbsp) {
continue;
}
- struct ds match = DS_EMPTY_INITIALIZER;
+ ds_clear(&match);
ds_put_format(&match, "outport == %s", op->json_key);
- if (lsp_is_enabled(op->nbs)) {
- build_port_security_l2("eth.dst", op->nbs->port_security,
- op->nbs->n_port_security, &match);
+ if (lsp_is_enabled(op->nbsp)) {
+ build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs,
+ &match);
ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
ds_cstr(&match), "output;");
} else {
ds_cstr(&match), "drop;");
}
- ds_destroy(&match);
-
- if (op->nbs->n_port_security) {
+ if (op->nbsp->n_port_security) {
build_port_security_ip(P_OUT, op, lflows);
}
}
+
+ ds_destroy(&match);
+ ds_destroy(&actions);
}
static bool
return !lrport->enabled || *lrport->enabled;
}
+/* Returns a string of the IP address of the router port 'op' that
+ * overlaps with 'ip_s". If one is not found, returns NULL.
+ *
+ * The caller must not free the returned string. */
+static const char *
+find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
+{
+ ovs_be32 ip;
+
+ if (!ip_parse(ip_s, &ip)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad ip address %s", ip_s);
+ return NULL;
+ }
+
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+ const struct ipv4_netaddr *na = &op->lrp_networks.ipv4_addrs[i];
+
+ if (!((na->network ^ ip) & na->mask)) {
+ /* There should be only 1 interface that matches the
+ * next hop. Otherwise, it's a configuration error,
+ * because subnets of router's interfaces should NOT
+ * overlap. */
+ return na->addr_s;
+ }
+ }
+
+ return NULL;
+}
+
static void
add_route(struct hmap *lflows, const struct ovn_port *op,
- ovs_be32 network, ovs_be32 mask, ovs_be32 gateway)
+ const char *lrp_addr_s, const char *network_s, int plen,
+ const char *gateway)
{
- char *match = xasprintf("ip4.dst == "IP_FMT"/"IP_FMT,
- IP_ARGS(network), IP_ARGS(mask));
+ char *match = xasprintf("ip4.dst == %s/%d", network_s, plen);
struct ds actions = DS_EMPTY_INITIALIZER;
ds_put_cstr(&actions, "ip.ttl--; reg0 = ");
if (gateway) {
- ds_put_format(&actions, IP_FMT, IP_ARGS(gateway));
+ ds_put_cstr(&actions, gateway);
} else {
ds_put_cstr(&actions, "ip4.dst");
}
- ds_put_format(&actions,
- "; "
- "reg1 = "IP_FMT"; "
- "eth.src = "ETH_ADDR_FMT"; "
+ ds_put_format(&actions, "; "
+ "reg1 = %s; "
+ "eth.src = %s; "
"outport = %s; "
+ "inport = \"\"; /* Allow sending out inport. */ "
"next;",
- IP_ARGS(op->ip), ETH_ADDR_ARGS(op->mac), op->json_key);
+ lrp_addr_s,
+ op->lrp_networks.ea_s,
+ op->json_key);
/* The priority here is calculated to implement longest-prefix-match
* routing. */
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING,
- count_1bits(ntohl(mask)), match, ds_cstr(&actions));
+ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, plen, match,
+ ds_cstr(&actions));
ds_destroy(&actions);
free(match);
}
struct hmap *ports,
const struct nbrec_logical_router_static_route *route)
{
- ovs_be32 prefix, next_hop, mask;
+ ovs_be32 prefix, nexthop, mask;
+ const char *lrp_addr_s;
/* Verify that next hop is an IP address with 32 bits mask. */
- char *error = ip_parse_masked(route->nexthop, &next_hop, &mask);
+ char *error = ip_parse_masked(route->nexthop, &nexthop, &mask);
if (error || mask != OVS_BE32_MAX) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
VLOG_WARN_RL(&rl, "bad next hop ip address %s", route->nexthop);
error = ip_parse_masked(route->ip_prefix, &prefix, &mask);
if (error || !ip_is_cidr(mask)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'network' in static routes %s",
+ VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
route->ip_prefix);
free(error);
return;
route->output_port, route->ip_prefix);
return;
}
+ lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
} else {
/* output_port is not specified, find the
* router port matching the next hop. */
continue;
}
- if (out_port->network
- && !((out_port->network ^ next_hop) & out_port->mask)) {
- /* There should be only 1 interface that matches the next hop.
- * Otherwise, it's a configuration error, because subnets of
- * router's interfaces should NOT overlap. */
+ lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
+ if (lrp_addr_s) {
break;
}
}
- if (i == od->nbr->n_ports) {
- /* There is no matched out port. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s",
- route->ip_prefix, route->nexthop);
- return;
- }
}
- add_route(lflows, out_port, prefix, mask, next_hop);
+ if (!lrp_addr_s) {
+ /* There is no matched out port. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s",
+ route->ip_prefix, route->nexthop);
+ return;
+ }
+
+ char *prefix_s = xasprintf(IP_FMT, IP_ARGS(prefix & mask));
+ add_route(lflows, out_port, lrp_addr_s, prefix_s,
+ ip_count_cidr_bits(mask), route->nexthop);
+ free(prefix_s);
+}
+
+static void
+op_put_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast)
+{
+ if (!add_bcast && op->lrp_networks.n_ipv4_addrs == 1) {
+ ds_put_format(ds, "%s", op->lrp_networks.ipv4_addrs[0].addr_s);
+ return;
+ }
+
+ ds_put_cstr(ds, "{");
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+ ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s);
+ if (add_bcast) {
+ ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s);
+ }
+ }
+ ds_chomp(ds, ' ');
+ ds_chomp(ds, ',');
+ ds_put_cstr(ds, "}");
}
static void
/* This flow table structure is documented in ovn-northd(8), so please
* update ovn-northd.8.xml if you change anything. */
+ struct ds match = DS_EMPTY_INITIALIZER;
+ struct ds actions = DS_EMPTY_INITIALIZER;
+
/* Logical router ingress table 0: Admission control framework. */
struct ovn_datapath *od;
HMAP_FOR_EACH (od, key_node, datapaths) {
/* Logical router ingress table 0: match (priority 50). */
struct ovn_port *op;
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbr) {
+ if (!op->nbrp) {
continue;
}
- if (!lrport_is_enabled(op->nbr)) {
+ if (!lrport_is_enabled(op->nbrp)) {
/* Drop packets from disabled logical ports (since logical flow
* tables are default-drop). */
continue;
}
- char *match = xasprintf(
- "(eth.mcast || eth.dst == "ETH_ADDR_FMT") && inport == %s",
- ETH_ADDR_ARGS(op->mac), op->json_key);
+ ds_clear(&match);
+ ds_put_format(&match, "(eth.mcast || eth.dst == %s) && inport == %s",
+ op->lrp_networks.ea_s, op->json_key);
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
- match, "next;");
- free(match);
+ ds_cstr(&match), "next;");
}
/* Logical router ingress table 1: IP Input. */
ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50,
"eth.bcast", "drop;");
- /* Drop IP multicast. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50,
- "ip4.mcast", "drop;");
-
/* TTL discard.
*
* XXX Need to send ICMP time exceeded if !ip.later_frag. */
- char *match = xasprintf("ip4 && ip.ttl == {0, 1}");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30, match, "drop;");
- free(match);
+ ds_clear(&match);
+ ds_put_cstr(&match, "ip4 && ip.ttl == {0, 1}");
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
+ ds_cstr(&match), "drop;");
/* Pass other traffic not already handled to the next table for
* routing. */
}
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbr) {
+ if (!op->nbrp) {
continue;
}
/* L3 admission control: drop packets that originate from an IP address
* owned by the router or a broadcast address known to the router
* (priority 100). */
- char *match = xasprintf("ip4.src == {"IP_FMT", "IP_FMT"}",
- IP_ARGS(op->ip), IP_ARGS(op->bcast));
+ ds_clear(&match);
+ ds_put_cstr(&match, "ip4.src == ");
+ op_put_networks(&match, op, true);
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
- match, "drop;");
- free(match);
+ ds_cstr(&match), "drop;");
/* ICMP echo reply. These flows reply to ICMP echo requests
* received for the router's IP address. Since packets only
* get here as part of the logical router datapath, the inport
* (i.e. the incoming locally attached net) does not matter.
* The ip.ttl also does not matter (RFC1812 section 4.2.2.9) */
- match = xasprintf(
- "ip4.dst == "IP_FMT" && icmp4.type == 8 && icmp4.code == 0",
- IP_ARGS(op->ip));
- char *actions = xasprintf(
- "ip4.dst = ip4.src; "
- "ip4.src = "IP_FMT"; "
+ ds_clear(&match);
+ ds_put_cstr(&match, "ip4.dst == ");
+ op_put_networks(&match, op, false);
+ ds_put_cstr(&match, " && icmp4.type == 8 && icmp4.code == 0");
+
+ ds_clear(&actions);
+ ds_put_format(&actions,
+ "ip4.dst <-> ip4.src; "
"ip.ttl = 255; "
"icmp4.type = 0; "
"inport = \"\"; /* Allow sending out inport. */ "
- "next; ",
- IP_ARGS(op->ip));
+ "next; ");
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- match, actions);
- free(match);
- free(actions);
+ ds_cstr(&match), ds_cstr(&actions));
/* ARP reply. These flows reply to ARP requests for the router's own
* IP address. */
- match = xasprintf(
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
- op->json_key, IP_ARGS(op->ip));
- actions = xasprintf(
- "eth.dst = eth.src; "
- "eth.src = "ETH_ADDR_FMT"; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = "ETH_ADDR_FMT"; "
- "arp.tpa = arp.spa; "
- "arp.spa = "IP_FMT"; "
- "outport = %s; "
- "inport = \"\"; /* Allow sending out inport. */ "
- "output;",
- ETH_ADDR_ARGS(op->mac),
- ETH_ADDR_ARGS(op->mac),
- IP_ARGS(op->ip),
- op->json_key);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- match, actions);
- free(match);
- free(actions);
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+ ds_clear(&match);
+ ds_put_format(&match,
+ "inport == %s && arp.tpa == %s && arp.op == 1",
+ op->json_key, op->lrp_networks.ipv4_addrs[i].addr_s);
+
+ ds_clear(&actions);
+ ds_put_format(&actions,
+ "eth.dst = eth.src; "
+ "eth.src = %s; "
+ "arp.op = 2; /* ARP reply */ "
+ "arp.tha = arp.sha; "
+ "arp.sha = %s; "
+ "arp.tpa = arp.spa; "
+ "arp.spa = %s; "
+ "outport = %s; "
+ "inport = \"\"; /* Allow sending out inport. */ "
+ "output;",
+ op->lrp_networks.ea_s,
+ op->lrp_networks.ea_s,
+ op->lrp_networks.ipv4_addrs[i].addr_s,
+ op->json_key);
+ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+ ds_cstr(&match), ds_cstr(&actions));
+ }
/* ARP handling for external IP addresses.
*
continue;
}
- match = xasprintf(
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
- op->json_key, IP_ARGS(ip));
- actions = xasprintf(
+ ds_clear(&match);
+ ds_put_format(&match,
+ "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
+ op->json_key, IP_ARGS(ip));
+
+ ds_clear(&actions);
+ ds_put_format(&actions,
"eth.dst = eth.src; "
- "eth.src = "ETH_ADDR_FMT"; "
+ "eth.src = %s; "
"arp.op = 2; /* ARP reply */ "
"arp.tha = arp.sha; "
- "arp.sha = "ETH_ADDR_FMT"; "
+ "arp.sha = %s; "
"arp.tpa = arp.spa; "
"arp.spa = "IP_FMT"; "
"outport = %s; "
"inport = \"\"; /* Allow sending out inport. */ "
"output;",
- ETH_ADDR_ARGS(op->mac),
- ETH_ADDR_ARGS(op->mac),
+ op->lrp_networks.ea_s,
+ op->lrp_networks.ea_s,
IP_ARGS(ip),
op->json_key);
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- match, actions);
- free(match);
- free(actions);
+ ds_cstr(&match), ds_cstr(&actions));
}
/* Drop IP traffic to this router, unless the router ip is used as
* SNAT ip. */
- bool snat_ip_is_router_ip = false;
+ ovs_be32 *nat_ips = xmalloc(sizeof *nat_ips * op->od->nbr->n_nat);
+ size_t n_nat_ips = 0;
for (int i = 0; i < op->od->nbr->n_nat; i++) {
const struct nbrec_nat *nat;
ovs_be32 ip;
continue;
}
- if (ip == op->ip) {
- snat_ip_is_router_ip = true;
- break;
+ nat_ips[n_nat_ips++] = ip;
+ }
+
+ ds_clear(&match);
+ ds_put_cstr(&match, "ip4.dst == {");
+ bool has_drop_ips = false;
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+ for (int j = 0; j < n_nat_ips; j++) {
+ if (op->lrp_networks.ipv4_addrs[i].addr == nat_ips[j]) {
+ continue;
+ }
}
+ ds_put_format(&match, "%s, ",
+ op->lrp_networks.ipv4_addrs[i].addr_s);
+ has_drop_ips = true;
}
+ ds_chomp(&match, ' ');
+ ds_chomp(&match, ',');
+ ds_put_cstr(&match, "}");
- if (!snat_ip_is_router_ip) {
- match = xasprintf("ip4.dst == "IP_FMT, IP_ARGS(op->ip));
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60, match,
- "drop;");
- free(match);
+ if (has_drop_ips) {
+ /* Drop IP traffic to this router. */
+ ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
+ ds_cstr(&match), "drop;");
}
+
+ free(nat_ips);
}
/* NAT in Gateway routers. */
}
}
-
- char *match, *actions;
-
/* Ingress UNSNAT table: It is for already established connections'
* reverse traffic. i.e., SNAT has already been done in egress
* pipeline and now the packet has entered the ingress pipeline as
* egress pipeline. */
if (!strcmp(nat->type, "snat")
|| !strcmp(nat->type, "dnat_and_snat")) {
- match = xasprintf("ip && ip4.dst == %s", nat->external_ip);
+ ds_clear(&match);
+ ds_put_format(&match, "ip && ip4.dst == %s", nat->external_ip);
ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
- match, "ct_snat; next;");
- free(match);
+ ds_cstr(&match), "ct_snat; next;");
}
/* Ingress DNAT table: Packets enter the pipeline with destination
/* Packet when it goes from the initiator to destination.
* We need to zero the inport because the router can
* send the packet back through the same interface. */
- match = xasprintf("ip && ip4.dst == %s", nat->external_ip);
- actions = xasprintf("inport = \"\"; ct_dnat(%s);",
- nat->logical_ip);
+ ds_clear(&match);
+ ds_put_format(&match, "ip && ip4.dst == %s", nat->external_ip);
+ ds_clear(&actions);
+ ds_put_format(&actions,"inport = \"\"; ct_dnat(%s);",
+ nat->logical_ip);
ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
- match, actions);
- free(match);
- free(actions);
+ ds_cstr(&match), ds_cstr(&actions));
}
/* Egress SNAT table: Packets enter the egress pipeline with
* address. */
if (!strcmp(nat->type, "snat")
|| !strcmp(nat->type, "dnat_and_snat")) {
- match = xasprintf("ip && ip4.src == %s", nat->logical_ip);
- actions = xasprintf("ct_snat(%s);", nat->external_ip);
+ ds_clear(&match);
+ ds_put_format(&match, "ip && ip4.src == %s", nat->logical_ip);
+ ds_clear(&actions);
+ ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
/* The priority here is calculated such that the
* nat->logical_ip with the longest mask gets a higher
* priority. */
ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
- count_1bits(ntohl(mask)) + 1, match, actions);
- free(match);
- free(actions);
+ count_1bits(ntohl(mask)) + 1,
+ ds_cstr(&match), ds_cstr(&actions));
}
}
* next-hop IP address (leaving ip4.dst, the packet’s final destination,
* unchanged), and advances to the next table for ARP resolution. */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbr) {
+ if (!op->nbrp) {
continue;
}
- add_route(lflows, op, op->network, op->mask, 0);
+ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+ add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
+ op->lrp_networks.ipv4_addrs[i].network_s,
+ op->lrp_networks.ipv4_addrs[i].plen, NULL);
+ }
}
+
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbr) {
continue;
route = od->nbr->static_routes[i];
build_static_route_flow(lflows, od, ports, route);
}
-
- if (od->gateway && od->gateway_port) {
- add_route(lflows, od->gateway_port, 0, 0, od->gateway);
- }
}
/* XXX destination unreachable */
* resolves the IP address in reg0 into an output port in outport and an
* Ethernet address in eth.dst. */
HMAP_FOR_EACH (op, key_node, ports) {
- if (op->nbr) {
+ if (op->nbrp) {
/* This is a logical router port. If next-hop IP address in 'reg0'
* matches ip address of this router port, then the packet is
* intended to eventually be sent to this logical port. Set the
*
* The packet is still in peer's logical pipeline. So the match
* should be on peer's outport. */
- if (op->nbr->peer) {
- struct ovn_port *peer = ovn_port_find(ports, op->nbr->peer);
+ if (op->nbrp->peer) {
+ struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
if (!peer) {
continue;
}
- if (!peer->ip || !op->ip) {
- continue;
- }
- char *match = xasprintf("outport == %s && reg0 == "IP_FMT,
- peer->json_key, IP_ARGS(op->ip));
- char *actions = xasprintf("eth.dst = "ETH_ADDR_FMT"; "
- "next;", ETH_ADDR_ARGS(op->mac));
+ ds_clear(&match);
+ ds_put_format(&match, "outport == %s && reg0 == ",
+ peer->json_key);
+ op_put_networks(&match, op, false);
+
+ ds_clear(&actions);
+ ds_put_format(&actions, "eth.dst = %s; next;",
+ op->lrp_networks.ea_s);
ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, match, actions);
- free(actions);
- free(match);
+ 100, ds_cstr(&match), ds_cstr(&actions));
}
- } else if (op->od->n_router_ports && strcmp(op->nbs->type, "router")) {
+ } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")) {
/* This is a logical switch port that backs a VM or a container.
* Extract its addresses. For each of the address, go through all
* the router ports attached to the switch (to which this port
* connects) and if the address in question is reachable from the
* router port, add an ARP entry in that router's pipeline. */
- for (size_t i = 0; i < op->nbs->n_addresses; i++) {
- struct lport_addresses laddrs;
- if (!extract_lsp_addresses(op->nbs->addresses[i], &laddrs,
- false)) {
- continue;
- }
-
- for (size_t k = 0; k < laddrs.n_ipv4_addrs; k++) {
- ovs_be32 ip = laddrs.ipv4_addrs[k].addr;
- for (size_t j = 0; j < op->od->n_router_ports; j++) {
+ for (size_t i = 0; i < op->n_lsp_addrs; i++) {
+ const char *ea_s = op->lsp_addrs[i].ea_s;
+ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
+ const char *ip_s = op->lsp_addrs[i].ipv4_addrs[j].addr_s;
+ for (size_t k = 0; k < op->od->n_router_ports; k++) {
/* Get the Logical_Router_Port that the
* Logical_Switch_Port is connected to, as
* 'peer'. */
const char *peer_name = smap_get(
- &op->od->router_ports[j]->nbs->options,
+ &op->od->router_ports[k]->nbsp->options,
"router-port");
if (!peer_name) {
continue;
}
- struct ovn_port *peer
- = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbr) {
+ struct ovn_port *peer = ovn_port_find(ports, peer_name);
+ if (!peer || !peer->nbrp) {
continue;
}
- /* Make sure that 'ip' is in 'peer''s network. */
- if ((ip ^ peer->network) & peer->mask) {
+ if (!find_lrp_member_ip(peer, ip_s)) {
continue;
}
- char *match = xasprintf(
- "outport == %s && reg0 == "IP_FMT,
- peer->json_key, IP_ARGS(ip));
- char *actions = xasprintf("eth.dst = "ETH_ADDR_FMT"; "
- "next;",
- ETH_ADDR_ARGS(laddrs.ea));
+ ds_clear(&match);
+ ds_put_format(&match, "outport == %s && reg0 == %s",
+ peer->json_key, ip_s);
+
+ ds_clear(&actions);
+ ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
ovn_lflow_add(lflows, peer->od,
- S_ROUTER_IN_ARP_RESOLVE,
- 100, match, actions);
- free(actions);
- free(match);
- break;
+ S_ROUTER_IN_ARP_RESOLVE, 100,
+ ds_cstr(&match), ds_cstr(&actions));
}
}
-
- free(laddrs.ipv4_addrs);
}
- } else if (!strcmp(op->nbs->type, "router")) {
+ } else if (!strcmp(op->nbsp->type, "router")) {
/* This is a logical switch port that connects to a router. */
/* The peer of this switch port is the router port for which
* ARP entries for all the other router ports connected to
* the switch in question. */
- const char *peer_name = smap_get(&op->nbs->options,
+ const char *peer_name = smap_get(&op->nbsp->options,
"router-port");
if (!peer_name) {
continue;
}
struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbr || !peer->ip) {
+ if (!peer || !peer->nbrp) {
continue;
}
- for (size_t j = 0; j < op->od->n_router_ports; j++) {
+ for (size_t i = 0; i < op->od->n_router_ports; i++) {
const char *router_port_name = smap_get(
- &op->od->router_ports[j]->nbs->options,
+ &op->od->router_ports[i]->nbsp->options,
"router-port");
struct ovn_port *router_port = ovn_port_find(ports,
router_port_name);
- if (!router_port || !router_port->nbr || !router_port->ip) {
+ if (!router_port || !router_port->nbrp) {
continue;
}
continue;
}
- if (!router_port->ip) {
- continue;
- }
- char *match = xasprintf("outport == %s && reg0 == "IP_FMT,
- peer->json_key,
- IP_ARGS(router_port->ip));
- char *actions = xasprintf("eth.dst = "ETH_ADDR_FMT"; next;",
- ETH_ADDR_ARGS(router_port->mac));
+ ds_clear(&match);
+ ds_put_format(&match, "outport == %s && reg0 == ",
+ peer->json_key);
+ op_put_networks(&match, router_port, false);
+
+ ds_clear(&actions);
+ ds_put_format(&actions, "eth.dst = %s; next;",
+ router_port->lrp_networks.ea_s);
ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, match, actions);
- free(actions);
- free(match);
+ 100, ds_cstr(&match), ds_cstr(&actions));
}
}
}
*
* Priority 100 rules deliver packets to enabled logical ports. */
HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbr) {
+ if (!op->nbrp) {
continue;
}
- if (!lrport_is_enabled(op->nbr)) {
+ if (!lrport_is_enabled(op->nbrp)) {
/* Drop packets to disabled logical ports (since logical flow
* tables are default-drop). */
continue;
}
- char *match = xasprintf("outport == %s", op->json_key);
+ ds_clear(&match);
+ ds_put_format(&match, "outport == %s", op->json_key);
ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100,
- match, "output;");
- free(match);
+ ds_cstr(&match), "output;");
}
+
+ ds_destroy(&match);
+ ds_destroy(&actions);
}
/* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
}
struct hmap lports_hmap;
const struct sbrec_port_binding *sb;
- const struct nbrec_logical_switch_port *nb;
+ const struct nbrec_logical_switch_port *nbsp;
struct lport_hash_node {
struct hmap_node node;
- const struct nbrec_logical_switch_port *nb;
+ const struct nbrec_logical_switch_port *nbsp;
} *hash_node;
hmap_init(&lports_hmap);
- NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(nb, ctx->ovnnb_idl) {
+ NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(nbsp, ctx->ovnnb_idl) {
hash_node = xzalloc(sizeof *hash_node);
- hash_node->nb = nb;
- hmap_insert(&lports_hmap, &hash_node->node, hash_string(nb->name, 0));
+ hash_node->nbsp = nbsp;
+ hmap_insert(&lports_hmap, &hash_node->node, hash_string(nbsp->name, 0));
}
SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
- nb = NULL;
+ nbsp = NULL;
HMAP_FOR_EACH_WITH_HASH(hash_node, node,
hash_string(sb->logical_port, 0),
&lports_hmap) {
- if (!strcmp(sb->logical_port, hash_node->nb->name)) {
- nb = hash_node->nb;
+ if (!strcmp(sb->logical_port, hash_node->nbsp->name)) {
+ nbsp = hash_node->nbsp;
break;
}
}
- if (!nb) {
+ if (!nbsp) {
/* The logical port doesn't exist for this port binding. This can
* happen under normal circumstances when ovn-northd hasn't gotten
* around to pruning the Port_Binding yet. */
continue;
}
- if (sb->chassis && (!nb->up || !*nb->up)) {
+ if (sb->chassis && (!nbsp->up || !*nbsp->up)) {
bool up = true;
- nbrec_logical_switch_port_set_up(nb, &up, 1);
- } else if (!sb->chassis && (!nb->up || *nb->up)) {
+ nbrec_logical_switch_port_set_up(nbsp, &up, 1);
+ } else if (!sb->chassis && (!nbsp->up || *nbsp->up)) {
bool up = false;
- nbrec_logical_switch_port_set_up(nb, &up, 1);
+ nbrec_logical_switch_port_set_up(nbsp, &up, 1);
}
}