Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions modules/infra/control/bond.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <gr_module.h>
#include <gr_port.h>
#include <gr_rcu.h>
#include <gr_vrf.h>

#include <rte_ether.h>

Expand Down Expand Up @@ -415,8 +416,14 @@ static int bond_init(struct iface *iface, const void *api_info) {
static int bond_fini(struct iface *iface) {
struct iface_info_bond *bond = iface_info_bond(iface);

while (bond->n_members > 0)
bond_detach_member(iface, bond->members[bond->n_members - 1].iface);
while (bond->n_members > 0) {
struct iface *member = bond->members[bond->n_members - 1].iface;
bond_detach_member(iface, member);
member->vrf_id = vrf_default_get_or_create();
if (member->vrf_id != GR_VRF_ID_UNDEF)
vrf_incref(member->vrf_id);
gr_event_push(GR_EVENT_IFACE_POST_RECONFIG, member);
}

gr_vec_free(bond->extra_macs);

Expand Down
2 changes: 2 additions & 0 deletions modules/infra/control/gr_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ int iface_set_promisc(struct iface *, bool enabled);
uint16_t ifaces_count(gr_iface_type_t type_id);
struct iface *iface_next(gr_iface_type_t type_id, const struct iface *prev);

uint16_t vrf_default_get_or_create(void);

// Get the VRF interface.
// vrf_id is the VRF interface ID.
struct iface *get_vrf_iface(uint16_t vrf_id);
Expand Down
4 changes: 2 additions & 2 deletions modules/infra/control/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ static int next_ifid(uint16_t *ifid) {
}

// Get or create the default VRF. Returns GR_VRF_DEFAULT_ID, or 0 on error.
static uint16_t get_or_create_default_vrf(void) {
uint16_t vrf_default_get_or_create(void) {
if (iface_from_id(GR_VRF_DEFAULT_ID) != NULL)
return GR_VRF_DEFAULT_ID;

Expand Down Expand Up @@ -174,7 +174,7 @@ struct iface *iface_create(const struct gr_iface *conf, const void *api_info) {

// Auto-create default VRF if no VRF specified
if (vrf_id == GR_IFACE_ID_UNDEF) {
vrf_id = get_or_create_default_vrf();
vrf_id = vrf_default_get_or_create();
if (vrf_id == GR_IFACE_ID_UNDEF)
goto fail;
}
Expand Down
5 changes: 5 additions & 0 deletions modules/infra/control/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ struct nexthop *nexthop_lookup_id(uint32_t nh_id) {

static void nh_cleanup_interface_cb(struct nexthop *nh, void *priv) {
if (nh->iface_id == (uintptr_t)priv) {
if (nh->type == GR_NH_T_L3) {
struct nexthop_info_l3 *l3 = nexthop_info_l3(nh);
if ((l3->flags & NH_LOCAL_ADDR_FLAGS) == NH_LOCAL_ADDR_FLAGS)
return; // addresses are cleaned per address family
}
nexthop_routes_cleanup(nh);
while (nh->ref_count)
nexthop_decref(nh);
Comment on lines 443 to 452
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to rely on GR_EVENT_IFACE_PRE_REMOVE and GR_EVENT_IFACE_REMOVE events to guarantee the order between nexthop_iface_cleanup and address-family-specific
handlers ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that (maybe) but the issue is that once GR_EVENT_IFACE_REMOVE fires, the interface_id is already unreachable/stale. So all nexthops that reference it point to NULL. As long as everyone checks the return value of iface_from_id(nh->iface_id) that's OK. But I'm not not 100% sure.

Expand Down
12 changes: 12 additions & 0 deletions modules/infra/control/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <gr_rcu.h>
#include <gr_vec.h>
#include <gr_vlan.h>
#include <gr_vrf.h>
#include <gr_worker.h>

#include <numa.h>
Expand Down Expand Up @@ -386,6 +387,17 @@ static int iface_port_fini(struct iface *iface) {
return errno_log(-ret, "worker_queue_reassign");
}

while ((i = iface_next(GR_IFACE_TYPE_UNDEF, i)) != NULL) {
if (i->domain_id == iface->id) {
i->vrf_id = vrf_default_get_or_create();
if (i->vrf_id != GR_VRF_ID_UNDEF)
vrf_incref(i->vrf_id);
i->mode = GR_IFACE_MODE_VRF;
i->domain_id = GR_IFACE_ID_UNDEF;
gr_event_push(GR_EVENT_IFACE_POST_RECONFIG, i);
}
}

port_ifaces[port->port_id] = NULL;

free(port->devargs);
Expand Down
7 changes: 7 additions & 0 deletions modules/infra/control/port_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <gr_module.h>
#include <gr_port.h>
#include <gr_rcu.h>
#include <gr_vrf.h>
#include <gr_worker.h>

// mocked types/functions
Expand All @@ -29,6 +30,12 @@ struct rte_rcu_qsbr *gr_datapath_rcu(void) {
static struct rte_rcu_qsbr rcu;
return &rcu;
}
uint16_t vrf_default_get_or_create(void) {
return 0;
}
int vrf_incref(uint16_t) {
return 0;
}
mock_func(struct iface *, iface_from_id(uint16_t));
mock_func(struct iface *, iface_next(gr_iface_type_t, const struct iface *));
mock_func(int, port_unplug(struct iface_info_port *));
Expand Down
7 changes: 7 additions & 0 deletions modules/infra/control/worker_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <gr_queue.h>
#include <gr_rcu.h>
#include <gr_vec.h>
#include <gr_vrf.h>
#include <gr_worker.h>

#include <numa.h>
Expand Down Expand Up @@ -50,6 +51,12 @@ struct rte_rcu_qsbr *gr_datapath_rcu(void) {
static struct rte_rcu_qsbr rcu;
return &rcu;
}
uint16_t vrf_default_get_or_create(void) {
return 0;
}
int vrf_incref(uint16_t) {
return 0;
}

struct iface *iface_from_id(uint16_t ifid) {
return ifid < ARRAY_DIM(ifaces) ? ifaces[ifid] : NULL;
Expand Down
2 changes: 1 addition & 1 deletion modules/infra/datapath/xconnect.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ xconnect_process(struct rte_graph *graph, struct rte_node *node, void **objs, ui

IFACE_STATS_INC(rx, mbuf, iface);

if (peer->type == GR_IFACE_TYPE_PORT) {
if (peer != NULL && peer->type == GR_IFACE_TYPE_PORT) {
port = iface_info_port(peer);
mbuf->port = port->port_id;
edge = OUTPUT;
Expand Down
17 changes: 12 additions & 5 deletions modules/ip/control/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,22 @@ static struct api_out addr_list(const void *request, struct api_ctx *ctx) {
return api_out(0, 0, NULL);
}

static void iface_pre_remove_cb(uint32_t /*event*/, const void *obj) {
static void iface_event_cb(uint32_t event, const void *obj) {
const struct iface *iface = obj;
struct nexthop_info_l3 *l3;
struct hoplist *ifaddrs;

ifaddrs = addr4_get_all(iface->id);
if (ifaddrs == NULL)
if (ifaddrs == NULL || gr_vec_len(ifaddrs->nh) == 0)
return;

if (event == GR_EVENT_IFACE_POST_RECONFIG) {
if (iface->mode == GR_IFACE_MODE_VRF && iface->vrf_id == ifaddrs->nh[0]->vrf_id)
return;
}

// interface is either being deleted, mode changed to !VRF, or changed to another VRF
// delete all configured addresses
while (gr_vec_len(ifaddrs->nh) > 0) {
l3 = nexthop_info_l3(ifaddrs->nh[gr_vec_len(ifaddrs->nh) - 1]);
addr4_delete(iface->id, l3->ipv4, l3->prefixlen);
Expand Down Expand Up @@ -323,9 +330,9 @@ static struct gr_module addr_module = {
};

static struct gr_event_subscription iface_pre_rm_subscription = {
.callback = iface_pre_remove_cb,
.ev_count = 1,
.ev_types = {GR_EVENT_IFACE_PRE_REMOVE},
.callback = iface_event_cb,
.ev_count = 2,
.ev_types = {GR_EVENT_IFACE_POST_RECONFIG, GR_EVENT_IFACE_PRE_REMOVE},
};
static struct gr_event_subscription iface_up_subscription = {
.callback = iface_up_cb,
Expand Down
76 changes: 51 additions & 25 deletions modules/ip6/control/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,40 +417,65 @@ static const struct rte_ipv6_addr well_known_mcast_addrs[] = {
GR_IPV6_ADDR_MLDV2,
};

static void ip6_iface_event_handler(uint32_t event, const void *obj) {
const struct nexthop_info_l3 *l3;
const struct iface *iface = obj;
static void ip6_iface_llocal_init(const struct iface *iface) {
struct rte_ipv6_addr link_local;
struct rte_ether_addr mac;
unsigned i;

if (iface_get_eth_addr(iface, &mac) == 0) {
rte_ipv6_llocal_from_ethernet(&link_local, &mac);
if (iface6_addr_add(iface, &link_local, 64) < 0)
errno_log(errno, "iface_addr_add");
}
for (i = 0; i < ARRAY_DIM(well_known_mcast_addrs); i++) {
if (mcast6_addr_add(iface, &well_known_mcast_addrs[i]) < 0)
LOG(INFO, "%s: mcast_addr_add: %s", iface->name, strerror(errno));
}
}

static void ip6_iface_addrs_flush(const struct iface *iface) {
const struct nexthop_info_l3 *l3;
const struct nexthop *nh;
struct hoplist *addrs;

addrs = &iface_addrs[iface->id];
while (gr_vec_len(addrs->nh) > 0) {
nh = addrs->nh[gr_vec_len(addrs->nh) - 1];
l3 = nexthop_info_l3(nh);
iface6_addr_del(iface, &l3->ipv6, l3->prefixlen);
}
addrs = &iface_mcast_addrs[iface->id];
while (gr_vec_len(addrs->nh) > 0) {
nh = addrs->nh[gr_vec_len(addrs->nh) - 1];
l3 = nexthop_info_l3(nh);
mcast6_addr_del(iface, &l3->ipv6);
}
}

static void ip6_iface_event_handler(uint32_t event, const void *obj) {
const struct iface *iface = obj;
const struct nexthop *nh;
struct hoplist *addrs;
unsigned i;

switch (event) {
case GR_EVENT_IFACE_POST_ADD:
if (iface_get_eth_addr(iface, &mac) == 0) {
rte_ipv6_llocal_from_ethernet(&link_local, &mac);
if (iface6_addr_add(iface, &link_local, 64) < 0)
errno_log(errno, "iface_addr_add");
}
for (i = 0; i < ARRAY_DIM(well_known_mcast_addrs); i++) {
if (mcast6_addr_add(iface, &well_known_mcast_addrs[i]) < 0)
LOG(INFO, "%s: mcast_addr_add: %s", iface->name, strerror(errno));
ip6_iface_llocal_init(iface);
break;
case GR_EVENT_IFACE_POST_RECONFIG:
if (iface->mode != GR_IFACE_MODE_VRF) {
// changing mode from VRF -> !VRF
ip6_iface_addrs_flush(iface);
} else if (gr_vec_len(iface_addrs[iface->id].nh) == 0) {
// changing mode from !VRF -> VRF
ip6_iface_llocal_init(iface);
} else if (iface_addrs[iface->id].nh[0]->vrf_id != iface->vrf_id) {
// changing to a different VRF
ip6_iface_addrs_flush(iface);
ip6_iface_llocal_init(iface);
}
break;
case GR_EVENT_IFACE_PRE_REMOVE:
addrs = &iface_addrs[iface->id];
while (gr_vec_len(addrs->nh) > 0) {
nh = addrs->nh[gr_vec_len(addrs->nh) - 1];
l3 = nexthop_info_l3(nh);
iface6_addr_del(iface, &l3->ipv6, l3->prefixlen);
}
addrs = &iface_mcast_addrs[iface->id];
while (gr_vec_len(addrs->nh) > 0) {
nh = addrs->nh[gr_vec_len(addrs->nh) - 1];
l3 = nexthop_info_l3(nh);
mcast6_addr_del(iface, &l3->ipv6);
}
ip6_iface_addrs_flush(iface);
break;
case GR_EVENT_IFACE_STATUS_UP:
addrs = &iface_addrs[iface->id];
Expand Down Expand Up @@ -512,9 +537,10 @@ static struct gr_module addr6_module = {

static struct gr_event_subscription iface_event_subscription = {
.callback = ip6_iface_event_handler,
.ev_count = 3,
.ev_count = 4,
.ev_types = {
GR_EVENT_IFACE_POST_ADD,
GR_EVENT_IFACE_POST_RECONFIG,
GR_EVENT_IFACE_PRE_REMOVE,
GR_EVENT_IFACE_STATUS_UP,
},
Expand Down
20 changes: 11 additions & 9 deletions smoke/config_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,27 @@ grcli stats show software
grcli stats show hardware
# Test address del cleans up connected routes
grcli address del 10.1.0.1/24 iface p3
grcli route show | grep -q '10.1.0.0/24' && fail "connected route should be removed after address del"
grcli route show | grep -qF '10.1.0.0/24' && fail "connected route should be removed after address del"
grcli address del 2346::1/24 iface p3
grcli route show | grep -q '2346::/24' && fail "connected route should be removed after address del"
grcli route show | grep -qF '2346::/24' && fail "connected route should be removed after address del"
# Test address flush with family filter
grcli address add 10.2.0.1/24 iface p3
grcli address add 2347::1/24 iface p3
grcli address show iface p3
grcli address flush ip4 iface p3
grcli address show iface p3 | grep -q '10.' && fail "p3 should have no IPv4 after ip4 flush"
grcli address show iface p3 | grep -q '2347::' || fail "p3 should still have IPv6 after ip4 flush"
grcli address show iface p3
grcli address show iface p3 | grep -qF '10.2.0.1/24' && fail "p3 should have no IPv4 after ip4 flush"
grcli address show iface p3 | grep -qF '2347::1/24' || fail "p3 should still have IPv6 after ip4 flush"
grcli address flush ip6 iface p3
grcli address show iface p3 | grep -q '2347::' && fail "p3 should have no user IPv6 after ip6 flush"
grcli address show iface p3 | grep -q 'fe80::' || fail "p3 should still have link-local after ip6 flush"
grcli address show iface p3 | grep -qF '2347::1/24' && fail "p3 should have no user IPv6 after ip6 flush"
grcli address show iface p3 | grep -qF 'fe80::' || fail "p3 should still have link-local after ip6 flush"
# Test flush all families (default)
grcli address add 10.3.0.1/24 iface p3
grcli address add 2348::1/24 iface p3
grcli address flush iface p3
grcli address show iface p3 | grep -q '10.' && fail "p3 should have no IPv4 after flush"
grcli address show iface p3 | grep -q '2348::' && fail "p3 should have no user IPv6 after flush"
grcli address show iface p3 | grep -q 'fe80::' || fail "p3 should still have link-local after flush"
grcli address show iface p3 | grep -qF '10.3.0.1/24' && fail "p3 should have no IPv4 after flush"
grcli address show iface p3 | grep -qF '2348::1/24' && fail "p3 should have no user IPv6 after flush"
grcli address show iface p3 | grep -qF 'fe80::' || fail "p3 should still have link-local after flush"

grcli nexthop del 42
grcli nexthop del 666
Expand Down
2 changes: 1 addition & 1 deletion smoke/config_vrf_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ grcli interface del p4
grcli interface del testvrf

# reserved name rejection
grcli interface add port p4 devargs net_null2,no-rx=1 name gr-loop99 \
grcli interface add port gr-loop99 devargs net_null2,no-rx=1 \
&& fail "reserved name gr-loop99 should be rejected"
grcli interface add vrf gr-loop99 \
&& fail "reserved name gr-loop99 should be rejected for VRF"
Expand Down
11 changes: 11 additions & 0 deletions smoke/ip6_add_del_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,14 @@ grcli address del 2001::1/64 iface p0
grcli address show
grcli address add 2001::1/64 iface p0
grcli address show

grcli interface add vrf foo
grcli interface set port p0 vrf foo
grcli address show
grcli nexthop show internal

grcli interface add port p1 devargs net_tap1,iface=x-p1
grcli interface set port p0 domain p1
grcli interface set port p1 domain p0

grcli nexthop show internal