Skip to content

Commit

Permalink
northd: Routing protocol port redirection.
Browse files Browse the repository at this point in the history
This change adds two new LRP options:
 * routing-protocol-redirect
 * routing-protocols

These allow redirection of a routing protocol traffic to
an Logical Switch Port. This enables external routing daemons
to listen on an interface bound to an LSP and effectively act
as if they were listening on (and speaking from) LRP's IP address.

Option 'routing-protocols' takes a comma-separated list of routing
protocols whose traffic should be redirected. Currently supported
are BGP (tcp 179) and BFD (udp 3784).

Option 'routing-protocol-redirect' expects a string with an LSP
name.

When both of these options are set, any traffic entering LS
that's destined for LRP's IP addresses (including IPv6 LLA) and
routing protocol's port number, is redirected to the LSP specified
in the 'routing-protocol-redirect' value.

NOTE: this feature is experimental and may be subject to
removal/change in the future.

Signed-off-by: Martin Kalcok <[email protected]>
  • Loading branch information
mkalcok authored and dceara committed Aug 9, 2024
1 parent 436a011 commit f996128
Show file tree
Hide file tree
Showing 6 changed files with 513 additions and 0 deletions.
217 changes: 217 additions & 0 deletions northd/northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -13935,6 +13935,221 @@ build_arp_resolve_flows_for_lrp(struct ovn_port *op,
}
}

static void
build_r_p_redirect_rule__(
const char *s_addr, const char *redirect_port_name, int protocol_port,
const char *proto, bool is_ipv6, struct ovn_port *ls_peer,
struct lflow_table *lflows, struct ds *match, struct ds *actions)
{
int ip_ver = is_ipv6 ? 6 : 4;
ds_clear(actions);
ds_put_format(actions, "outport = \"%s\"; output;", redirect_port_name);

/* Redirect packets in the input pipeline destined for LR's IP
* and the routing protocol's port to the LSP specified in
* 'routing-protocol-redirect' option.*/
ds_clear(match);
ds_put_format(match, "ip%d.dst == %s && %s.dst == %d", ip_ver, s_addr,
proto, protocol_port);
ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
ds_cstr(match),
ds_cstr(actions),
ls_peer->lflow_ref);

/* To accomodate "peer" nature of the routing daemons, redirect also
* replies to the daemons' client requests. */
ds_clear(match);
ds_put_format(match, "ip%d.dst == %s && %s.src == %d", ip_ver, s_addr,
proto, protocol_port);
ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
ds_cstr(match),
ds_cstr(actions),
ls_peer->lflow_ref);
}

static void
apply_r_p_redirect__(
const char *s_addr, const char *redirect_port_name, int protocol_flags,
bool is_ipv6, struct ovn_port *ls_peer, struct lflow_table *lflows,
struct ds *match, struct ds *actions)
{
if (protocol_flags & REDIRECT_BGP) {
build_r_p_redirect_rule__(s_addr, redirect_port_name, 179, "tcp",
is_ipv6, ls_peer, lflows, match, actions);
}

if (protocol_flags & REDIRECT_BFD) {
build_r_p_redirect_rule__(s_addr, redirect_port_name, 3784, "udp",
is_ipv6, ls_peer, lflows, match, actions);
}

/* Because the redirected port shares IP and MAC addresses with the LRP,
* special consideration needs to be given to the signaling protocols. */
if (is_ipv6) {
/* Ensure that redirect port receives copy of NA messages destined to
* its IP.*/
ds_clear(match);
ds_clear(actions);
ds_put_format(actions, "clone { outport = \"%s\"; output; };",
redirect_port_name);
ds_put_format(match, "ip6.dst == %s && nd_na", s_addr);
ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
ds_cstr(match),
ds_cstr(actions),
ls_peer->lflow_ref);
} else {
/* Ensure that redirect port receives copy of ARP replies destined to
* its IP */
ds_clear(match);
ds_clear(actions);
ds_put_format(actions, "clone { outport = \"%s\"; output; };",
redirect_port_name);
ds_put_format(match, "arp.op == 2 && arp.tpa == %s", s_addr);
ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
ds_cstr(match),
ds_cstr(actions),
ls_peer->lflow_ref);

}
}

static int
parse_redirected_routing_protocols(struct ovn_port *lrp) {
int redirected_protocol_flags = 0;
const char *redirect_protocols = smap_get(&lrp->nbrp->options,
"routing-protocols");
if (redirect_protocols == NULL) {
return redirected_protocol_flags;
}

char *proto;
char *save_ptr = NULL;
char *tokstr = xstrdup(redirect_protocols);
for (proto = strtok_r(tokstr, ",", &save_ptr); proto != NULL;
proto = strtok_r(NULL, ",", &save_ptr)) {
if (!strcmp(proto, "BGP")) {
redirected_protocol_flags |= REDIRECT_BGP;
continue;
}

if (!strcmp(proto, "BFD")) {
redirected_protocol_flags |= REDIRECT_BFD;
continue;
}
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Option 'routing-protocols' encountered unknown "
"value %s",
proto);
}
free(tokstr);
return redirected_protocol_flags;
}

static void
build_lrouter_routing_protocol_redirect(
struct ovn_port *op, struct lflow_table *lflows,
struct ds *match, struct ds *actions)
{
/* LRP has to have a peer.*/
if (op->peer == NULL) {
return;
}
/* LRP has to have NB record.*/
if (op->nbrp == NULL) {
return;
}

/* Proceed only for LRPs that have 'routing-protocol-redirect' option set.
* Value of this option is the name of LSP to which the routing protocol
* traffic will be redirected. */
const char *redirect_port = smap_get(&op->nbrp->options,
"routing-protocol-redirect");
if (redirect_port == NULL) {
return;
}

if (op->cr_port != NULL) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' is not "
"supported on Distributed Gateway Port '%s'",
op->key);
return;
}

/* Ensure that LSP, to which the routing protocol traffic is redirected,
* exists.*/
struct ovn_port *peer_lsp;
bool redirect_port_exists = false;
HMAP_FOR_EACH (peer_lsp, dp_node, &op->peer->od->ports) {
size_t peer_lsp_s = strlen(peer_lsp->key);
if (peer_lsp_s == strlen(redirect_port)
&& !strncmp(peer_lsp->key, redirect_port, peer_lsp_s)){
redirect_port_exists = true;
break;
}
}

if (!redirect_port_exists) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' set on Logical "
"Router Port '%s' refers to non-existent Logical "
"Switch Port. Routing protocol redirecting won't be "
"configured.",
op->key);
return;
}


int redirected_protocols = parse_redirected_routing_protocols(op);
if (!redirected_protocols) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' is set on "
"Logical Router Port '%s' but no known protocols "
"were set via 'routing-protocols' options. This "
"configuration has no effect.",
op->key);
return;
}

/* Redirecte traffic destined for LRP's IPs and the specified routing
* protocol ports to the port defined in 'routing-protocol-redirect'
* option.*/
for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
const char *ip_s = op->lrp_networks.ipv4_addrs[i].addr_s;
apply_r_p_redirect__(ip_s, redirect_port, redirected_protocols, false,
op->peer, lflows,match, actions);
}
for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
const char *ip_s = op->lrp_networks.ipv6_addrs[i].addr_s;
apply_r_p_redirect__(ip_s, redirect_port, redirected_protocols, true,
op->peer, lflows,match, actions);
}

/* Drop ARP replies and IPv6 RA/NA packets originating from
* 'routing-protocol-redirect' LSP. As this port shares IP and MAC
* addresses with LRP, we don't want to create duplicates.*/
ds_clear(match);
ds_put_format(match, "inport == \"%s\" && arp.op == 2", redirect_port);
ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
ds_cstr(match),
REGBIT_PORT_SEC_DROP " = 1; next;",
op->peer->lflow_ref);

ds_clear(match);
ds_put_format(match, "inport == \"%s\" && nd_na", redirect_port);
ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
ds_cstr(match),
REGBIT_PORT_SEC_DROP " = 1; next;",
op->peer->lflow_ref);

ds_clear(match);
ds_put_format(match, "inport == \"%s\" && nd_ra", redirect_port);
ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
ds_cstr(match),
REGBIT_PORT_SEC_DROP " = 1; next;",
op->peer->lflow_ref);
}

/* This function adds ARP resolve flows related to a LSP. */
static void
build_arp_resolve_flows_for_lsp(
Expand Down Expand Up @@ -16900,6 +17115,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
op->lflow_ref);
build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
&lsi->actions, op->lflow_ref);
build_lrouter_routing_protocol_redirect(op, lsi->lflows,
&lsi->match, &lsi->actions);
}

static void *
Expand Down
7 changes: 7 additions & 0 deletions northd/northd.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ ovn_datapath_find_by_key(struct hmap *datapaths, uint32_t dp_key);

bool od_has_lb_vip(const struct ovn_datapath *od);

/* List of routing and routing-related protocols which
* OVN is capable of redirecting from LRP to specific LSP. */
enum redirected_routing_protcol_flag_type {
REDIRECT_BGP = (1 << 0),
REDIRECT_BFD = (1 << 1),
};

struct tracked_ovn_ports {
/* tracked created ports.
* hmapx node data is 'struct ovn_port *' */
Expand Down
54 changes: 54 additions & 0 deletions northd/ovn-northd.8.xml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,32 @@
dropped in the next stage.
</li>

<li>
<p>
For each logical port that's defined as a target of routing protocol
redirecting (via <code>routing-protocol-redirect</code> option set on
Logical Router Port), a filter is set in place that disallows
following traffic exiting this port:
</p>
<ul>
<li>
ARP replies
</li>
<li>
IPv6 Neighbor Discovery - Router Advertisements
</li>
<li>
IPv6 Neighbor Discovery - Neighbor Advertisements
</li>
</ul>
<p>
Since this port shares IP and MAC addresses with the Logical Router
Port, we wan't to prevent duplicate replies and advertisements. This
is achieved by a rule with priority 80 that sets
<code>REGBIT_PORT_SEC_DROP" = 1; next;"</code>.
</p>
</li>

<li>
For each (enabled) vtep logical port, a priority 70 flow is added which
matches on all packets and applies the action
Expand Down Expand Up @@ -2002,6 +2028,34 @@ output;
on the logical switch.
</li>

<li>
<p>
For any logical port that's defined as a target of routing protocol
redirecting (via <code>routing-protocol-redirect</code> option set on
Logical Router Port), we redirect the traffic related to protocols
specified in <code>routing-protocols</code> option. It's acoomplished
with following priority-100 flows:
<p>
<ul>
<li>
Flows that match Logical Router Port's IPs and destination port of
the routing daemon are redirected to this port to allow external
peers' connection to the daemon listening on this port.
</li>
<li>
Flows that match Logical Router Port's IPs and source port of
the routing daemon are redirected to this port to allow replies
from the peers.
</li>
</ul>
<p>
In addition to this, we add priority-100 rules that
<code>clone</code> ARP replies and IPv6 Neighbor Advertisements to
this port as well. These allow to build proper ARP/IPv6 neighbor
list on this port.
</p>
</li>

<li>
Priority-90 flows for transit switches that forward registered
IP multicast traffic to their corresponding multicast group , which
Expand Down
42 changes: 42 additions & 0 deletions ovn-nb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3575,6 +3575,48 @@ or
</p>
</column>

<column name="options" key="routing-protocol-redirect"
type='{"type": "string"}'>
<p>
NOTE: this feature is experimental and may be subject to
removal/change in the future.
</p>
<p>
This option expects a name of a Logical Switch Port that's present
in the peer's Logical Switch. If set, it causes any traffic
that's destined for Logical Router Port's IP addresses (including
its IPv6 LLA) and the ports associated with routing protocols defined
ip <code>routing-protocols</code> option, to be redirected
to the specified Logical Switch Port.

This allows external routing daemons to be bound to a port in OVN's
Logical Switch and act as if they were listening on Logical Router
Port's IP addresses.
</p>
</column>

<column name="options" key="routing-protocols" type='{"type": "string"}'>
<p>
NOTE: this feature is experimental and may be subject to
removal/change in the future.
</p>
<p>
This option expects a comma-separated list of routing, and
routing-related protocols, whose control plane traffic will be
redirected to a port specified in
<code>routing-protocol-redirect</code> option. Currently supported
options are:
</p>
<ul>
<li>
<code>BGP</code> (forwards TCP port 179)
</li>
<li>
<code>BFD</code> (forwards UDP port 3748)
</li>
<ul>
</column>

<column name="options" key="gateway_mtu_bypass">
<p>
When configured, represents a match expression, in the same
Expand Down
Loading

0 comments on commit f996128

Please sign in to comment.