Skip to content

Commit

Permalink
OVN: Add IGMP SB definitions and ovn-controller support
Browse files Browse the repository at this point in the history
A new IP_Multicast table is added to Southbound DB. This table stores the
multicast related configuration for each datapath. Each row will be
populated by ovn-northd and will control:
- if IGMP Snooping is enabled or not, the snooping table size and multicast
  group idle timeout.
- if IGMP Querier is enabled or not (only if snooping is enabled too), query
  interval, query source addresses (Ethernet and IP) and the max-response
  field to be stored in outgoing queries.
- an additional "seq_no" column is added such that ovn-sbctl or if needed a
  CMS can flush currently learned groups. This can be achieved by incrementing
  the "seq_no" value.

A new IGMP_Group table is added to Southbound DB. This table stores all the
multicast groups learned by ovn-controllers. The table is indexed by
datapath, group address and chassis. For a learned multicast group on a
specific datapath each ovn-controller will store its own row in this table.
Each row contains the list of chassis-local ports on which the group was
learned. Rows in the IGMP_Group table are updated or deleted only by the
ovn-controllers that created them.

A new action ("igmp") is added to punt IGMP packets on a specific logical
switch datapath to ovn-controller if IGMP snooping is enabled.

Per datapath IGMP multicast snooping support is added to pinctrl:
- incoming IGMP reports are processed and multicast groups are maintained
  (using the OVS mcast-snooping library).
- each OVN controller syncs its in-memory IGMP groups to the Southbound DB
  in the IGMP_Group table.
- pinctrl also sends periodic IGMPv3 general queries for all datapaths where
  querier is enabled.

Signed-off-by: Mark Michelson <[email protected]>
Co-authored-by: Mark Michelson <[email protected]>
Signed-off-by: Dumitru Ceara <[email protected]>
Acked-by: Mark Michelson <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
2 people authored and blp committed Jul 16, 2019
1 parent 1db4445 commit c89f5f1
Show file tree
Hide file tree
Showing 17 changed files with 1,312 additions and 6 deletions.
7 changes: 7 additions & 0 deletions include/ovn/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct ovn_extend_table;
OVNACT(ICMP4, ovnact_nest) \
OVNACT(ICMP4_ERROR, ovnact_nest) \
OVNACT(ICMP6, ovnact_nest) \
OVNACT(IGMP, ovnact_null) \
OVNACT(TCP_RESET, ovnact_nest) \
OVNACT(ND_NA, ovnact_nest) \
OVNACT(ND_NA_ROUTER, ovnact_nest) \
Expand Down Expand Up @@ -498,6 +499,12 @@ enum action_opcode {

/* "trigger_event (event_type)" */
ACTION_OPCODE_EVENT,

/* "igmp".
*
* Snoop IGMP, learn the multicast participants
*/
ACTION_OPCODE_IGMP,
};

/* Header. */
Expand Down
2 changes: 2 additions & 0 deletions ovn/controller/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ ovn_controller_ovn_controller_SOURCES = \
ovn/controller/encaps.h \
ovn/controller/ha-chassis.c \
ovn/controller/ha-chassis.h \
ovn/controller/ip-mcast.c \
ovn/controller/ip-mcast.h \
ovn/controller/lflow.c \
ovn/controller/lflow.h \
ovn/controller/lport.c \
Expand Down
164 changes: 164 additions & 0 deletions ovn/controller/ip-mcast.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* Copyright (c) 2019, Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <config.h>

#include "ip-mcast.h"
#include "lport.h"
#include "ovn/lib/ovn-sb-idl.h"

/*
* Used for (faster) updating of IGMP_Group ports.
*/
struct igmp_group_port {
struct hmap_node hmap_node;
const struct sbrec_port_binding *port;
};

struct ovsdb_idl_index *
igmp_group_index_create(struct ovsdb_idl *idl)
{
const struct ovsdb_idl_index_column cols[] = {
{ .column = &sbrec_igmp_group_col_address },
{ .column = &sbrec_igmp_group_col_datapath },
{ .column = &sbrec_igmp_group_col_chassis },
};

return ovsdb_idl_index_create(idl, cols, ARRAY_SIZE(cols));
}

/* Looks up an IGMP group based on an IPv4 (mapped in IPv6) or IPv6 'address'
* and 'datapath'.
*/
const struct sbrec_igmp_group *
igmp_group_lookup(struct ovsdb_idl_index *igmp_groups,
const struct in6_addr *address,
const struct sbrec_datapath_binding *datapath,
const struct sbrec_chassis *chassis)
{
char addr_str[INET6_ADDRSTRLEN];

if (!ipv6_string_mapped(addr_str, address)) {
return NULL;
}

struct sbrec_igmp_group *target =
sbrec_igmp_group_index_init_row(igmp_groups);

sbrec_igmp_group_index_set_address(target, addr_str);
sbrec_igmp_group_index_set_datapath(target, datapath);
sbrec_igmp_group_index_set_chassis(target, chassis);

const struct sbrec_igmp_group *g =
sbrec_igmp_group_index_find(igmp_groups, target);
sbrec_igmp_group_index_destroy_row(target);
return g;
}

/* Creates and returns a new IGMP group based on an IPv4 (mapped in IPv6) or
* IPv6 'address', 'datapath' and 'chassis'.
*/
struct sbrec_igmp_group *
igmp_group_create(struct ovsdb_idl_txn *idl_txn,
const struct in6_addr *address,
const struct sbrec_datapath_binding *datapath,
const struct sbrec_chassis *chassis)
{
char addr_str[INET6_ADDRSTRLEN];

if (!ipv6_string_mapped(addr_str, address)) {
return NULL;
}

struct sbrec_igmp_group *g = sbrec_igmp_group_insert(idl_txn);

sbrec_igmp_group_set_address(g, addr_str);
sbrec_igmp_group_set_datapath(g, datapath);
sbrec_igmp_group_set_chassis(g, chassis);

return g;
}

void
igmp_group_update_ports(const struct sbrec_igmp_group *g,
struct ovsdb_idl_index *datapaths,
struct ovsdb_idl_index *port_bindings,
const struct mcast_snooping *ms OVS_UNUSED,
const struct mcast_group *mc_group)
OVS_REQ_RDLOCK(ms->rwlock)
{
struct igmp_group_port *old_ports_storage =
(g->n_ports ? xmalloc(g->n_ports * sizeof *old_ports_storage) : NULL);

struct hmap old_ports = HMAP_INITIALIZER(&old_ports);

for (size_t i = 0; i < g->n_ports; i++) {
struct igmp_group_port *old_port = &old_ports_storage[i];

old_port->port = g->ports[i];
hmap_insert(&old_ports, &old_port->hmap_node,
old_port->port->tunnel_key);
}

struct mcast_group_bundle *bundle;
uint64_t dp_key = g->datapath->tunnel_key;

LIST_FOR_EACH (bundle, bundle_node, &mc_group->bundle_lru) {
uint32_t port_key = (uintptr_t)bundle->port;
const struct sbrec_port_binding *sbrec_port =
lport_lookup_by_key(datapaths, port_bindings, dp_key, port_key);
if (!sbrec_port) {
continue;
}

struct hmap_node *node = hmap_first_with_hash(&old_ports, port_key);
if (!node) {
sbrec_igmp_group_update_ports_addvalue(g, sbrec_port);
} else {
hmap_remove(&old_ports, node);
}
}

struct igmp_group_port *igmp_port;
HMAP_FOR_EACH_POP (igmp_port, hmap_node, &old_ports) {
sbrec_igmp_group_update_ports_delvalue(g, igmp_port->port);
}

free(old_ports_storage);
hmap_destroy(&old_ports);
}

void
igmp_group_delete(const struct sbrec_igmp_group *g)
{
sbrec_igmp_group_delete(g);
}

bool
igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
struct ovsdb_idl_index *igmp_groups)
{
const struct sbrec_igmp_group *g;

if (!ovnsb_idl_txn) {
return true;
}

SBREC_IGMP_GROUP_FOR_EACH_BYINDEX (g, igmp_groups) {
igmp_group_delete(g);
}

return true;
}
52 changes: 52 additions & 0 deletions ovn/controller/ip-mcast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Copyright (c) 2019, Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef OVN_IP_MCAST_H
#define OVN_IP_MCAST_H 1

#include "mcast-snooping.h"

struct ovsdb_idl;
struct ovsdb_idl_txn;

struct sbrec_chassis;
struct sbrec_datapath_binding;

struct ovsdb_idl_index *igmp_group_index_create(struct ovsdb_idl *);
const struct sbrec_igmp_group *igmp_group_lookup(
struct ovsdb_idl_index *igmp_groups,
const struct in6_addr *address,
const struct sbrec_datapath_binding *datapath,
const struct sbrec_chassis *chassis);

struct sbrec_igmp_group *igmp_group_create(
struct ovsdb_idl_txn *idl_txn,
const struct in6_addr *address,
const struct sbrec_datapath_binding *datapath,
const struct sbrec_chassis *chassis);

void igmp_group_update_ports(const struct sbrec_igmp_group *g,
struct ovsdb_idl_index *datapaths,
struct ovsdb_idl_index *port_bindings,
const struct mcast_snooping *ms,
const struct mcast_group *mc_group)
OVS_REQ_RDLOCK(ms->rwlock);

void igmp_group_delete(const struct sbrec_igmp_group *g);

bool igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
struct ovsdb_idl_index *igmp_groups);

#endif /* ovn/controller/ip-mcast.h */
23 changes: 23 additions & 0 deletions ovn/controller/ovn-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "openvswitch/dynamic-string.h"
#include "encaps.h"
#include "fatal-signal.h"
#include "ip-mcast.h"
#include "openvswitch/hmap.h"
#include "lflow.h"
#include "lib/vswitch-idl.h"
Expand All @@ -43,6 +44,7 @@
#include "ovn/actions.h"
#include "ovn/lib/chassis-index.h"
#include "ovn/lib/extend-table.h"
#include "ovn/lib/ip-mcast-index.h"
#include "ovn/lib/ovn-sb-idl.h"
#include "ovn/lib/ovn-util.h"
#include "patch.h"
Expand Down Expand Up @@ -135,6 +137,10 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
*
* Monitor Controller_Event rows for local chassis.
*
* Monitor IP_Multicast for local datapaths.
*
* Monitor IGMP_Groups for local chassis.
*
* We always monitor patch ports because they allow us to see the linkages
* between related logical datapaths. That way, when we know that we have
* a VIF on a particular logical switch, we immediately know to monitor all
Expand All @@ -145,6 +151,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg);
struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns);
struct ovsdb_idl_condition ce = OVSDB_IDL_CONDITION_INIT(&ce);
struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast);
struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp);
sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "patch");
/* XXX: We can optimize this, if we find a way to only monitor
* ports that have a Gateway_Chassis that point's to our own
Expand All @@ -171,6 +179,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,

sbrec_controller_event_add_clause_chassis(&ce, OVSDB_F_EQ,
&chassis->header_.uuid);
sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
&chassis->header_.uuid);
}
if (local_ifaces) {
const char *name;
Expand All @@ -190,6 +200,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
sbrec_mac_binding_add_clause_datapath(&mb, OVSDB_F_EQ, uuid);
sbrec_multicast_group_add_clause_datapath(&mg, OVSDB_F_EQ, uuid);
sbrec_dns_add_clause_datapaths(&dns, OVSDB_F_INCLUDES, &uuid, 1);
sbrec_ip_multicast_add_clause_datapath(&ip_mcast, OVSDB_F_EQ,
uuid);
}
}
sbrec_port_binding_set_condition(ovnsb_idl, &pb);
Expand All @@ -198,12 +210,16 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
sbrec_multicast_group_set_condition(ovnsb_idl, &mg);
sbrec_dns_set_condition(ovnsb_idl, &dns);
sbrec_controller_event_set_condition(ovnsb_idl, &ce);
sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast);
sbrec_igmp_group_set_condition(ovnsb_idl, &igmp);
ovsdb_idl_condition_destroy(&pb);
ovsdb_idl_condition_destroy(&lf);
ovsdb_idl_condition_destroy(&mb);
ovsdb_idl_condition_destroy(&mg);
ovsdb_idl_condition_destroy(&dns);
ovsdb_idl_condition_destroy(&ce);
ovsdb_idl_condition_destroy(&ip_mcast);
ovsdb_idl_condition_destroy(&igmp);
}

static const char *
Expand Down Expand Up @@ -1747,6 +1763,10 @@ main(int argc, char *argv[])
= ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
&sbrec_mac_binding_col_logical_port,
&sbrec_mac_binding_col_ip);
struct ovsdb_idl_index *sbrec_ip_multicast
= ip_mcast_index_create(ovnsb_idl_loop.idl);
struct ovsdb_idl_index *sbrec_igmp_group
= igmp_group_index_create(ovnsb_idl_loop.idl);

ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
Expand Down Expand Up @@ -1988,6 +2008,8 @@ main(int argc, char *argv[])
sbrec_port_binding_by_key,
sbrec_port_binding_by_name,
sbrec_mac_binding_by_lport_ip,
sbrec_igmp_group,
sbrec_ip_multicast,
sbrec_dns_table_get(ovnsb_idl_loop.idl),
sbrec_controller_event_table_get(
ovnsb_idl_loop.idl),
Expand Down Expand Up @@ -2121,6 +2143,7 @@ main(int argc, char *argv[])
done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis);
done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
done = encaps_cleanup(ovs_idl_txn, br_int) && done;
done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) && done;
if (done) {
poll_immediate_wake();
}
Expand Down
Loading

0 comments on commit c89f5f1

Please sign in to comment.