Page MenuHomeFreeBSD

route/fib_algo: Fix nexthop index collision across families
Needs ReviewPublic

Authored by pouria on Wed, Sep 9, 10:32 PM.
Tags
None
Referenced Files
F172325723: D59552.id186311.diff
Thu, Sep 17, 6:15 PM
F172309650: D59552.diff
Thu, Sep 17, 3:45 PM
Unknown Object (File)
Wed, Sep 16, 11:31 AM
Unknown Object (File)
Tue, Sep 15, 3:30 PM
Unknown Object (File)
Tue, Sep 15, 6:51 AM
Unknown Object (File)
Mon, Sep 14, 7:27 PM
Unknown Object (File)
Sun, Sep 13, 1:27 PM
Unknown Object (File)
Sun, Sep 13, 12:45 PM
Subscribers

Details

Reviewers
melifaro
glebius
markj
bms
Group Reviewers
network
Summary

fib_algo indexes its idx->nhop array by the nexthop index with
assumption of its uniqueness. Which is true except for IPv4 over
IPv6 nexthops.
Give each index space its own segment within the same array and
offset the index by the segment base. Segments are created on demand
and sized independently, so the rib's own family keeps base 0 and
tables without cross-family nexthops index exactly as before.

MFC after: 2 weeks

Test Plan

Before this patch you can trigger this panic under INVARIANTS by using overlapping idx across families

panic: index table contains whong nh
# netstat -on4
Nexthop data

Internet:
Idx   IFA                Gateway            Flags         Netif  Refcnt
1     127.0.0.1          lo0/resolve        H               lo0     2
2     172.23.1.101       vtnet0/resolve                  vtnet0     2
3     127.0.0.1          lo0/resolve        HS              lo0     2
4     172.23.1.101       172.23.1.1         GS           vtnet0     2
# netstat -on6
Nexthop data

Internet6:
Idx   IFA                           Gateway                       Flags         Netif  Refcnt
1     ::1                           lo0/resolve                   HS              lo0     2
2     fe80::1%lo0                   lo0/resolve                   HS              lo0     2
3     fe80::1%lo0                   lo0/resolve                                   lo0     2
4     ::1                           lo0/resolve                   HS              lo0     3
5     fdb5:c59b:114e::a             vtnet0/resolve                             vtnet0     8
6     ::1                           lo0/resolve                   RS              lo0     5
# route -n4 add -net 192.0.2.0/24 -gateway 172.23.1.102
add net 192.0.2.0: gateway 172.23.1.102 fib 0
# route -n4 add -net 192.0.3.0/24 -gateway 172.23.1.103
add net 192.0.3.0: gateway 172.23.1.103 fib 0
# route -n4 add -net 192.0.4.0/24 -gateway 172.23.1.104
add net 192.0.4.0: gateway 172.23.1.104 fib 0
# route -n4 add -net 192.0.5.0/24 -inet6 -gateway fdb5:c59b:114e::a
add net 192.0.5.0: gateway fdb5:c59b:114e::a fib 0

## Delete the same index in another family, try multiple time to find the right time.
# route -n4 del -net 192.0.4.0/24 -gateway 172.23.1.104
panic: index table contains whong nh
cpuid = 5
time = 1788995613
KDB: stack backtrace:
#0 0xffffffff80ca0745 at kdb_backtrace+0x75
#1 0xffffffff80c4ab59 at vpanic+0x149
#2 0xffffffff80c4aa03 at panic+0x43
#3 0xffffffff80dd935b at fib_unref_nhop+0x13b
#4 0xffffffff80ddf080 at rib_notify+0x50
#5 0xffffffff80ddb1f6 at rt_delete_conditional+0xf6
#6 0xffffffff80ddaed9 at rib_del_route_px+0x1e9
#7 0xffffffff80f00a64 at rtnl_handle_delroute+0x124
#8 0xffffffff80ef73dd at rtnl_handle_message+0x13d
#9 0xffffffff80ef5c0a at nl_receive_message+0x12a
#10 0xffffffff80ef5545 at nl_taskqueue_handler+0x3f5
#11 0xffffffff80cbaa5b at taskqueue_run_locked+0x1bb
#12 0xffffffff80cbba93 at taskqueue_thread_loop+0xd3
#13 0xffffffff80bf7352 at fork_exit+0x82
#14 0xffffffff811afece at fork_trampoline+0xe
KDB: enter: panic

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 76722
Build 73605: arc lint + arc unit

Event Timeline

pouria held this revision as a draft.

Move estimate_nhop_scale to after fd->family

pouria published this revision for review.Wed, Sep 9, 11:38 PM

FYI: @bms, I redesigned the current fib_algo to support neighbor families other than extra 4o6. Subhash Murmu (MPLS GSoC student) can increase FD_MAX_NH_AF and reuse it for its own dataplane.

I think I should explain what happens here, I'll start from relationships between routing modules:

In the networking industry there is a clear separation between RIB and FIB, at least as far as vendors are concerned.
In the FreeBSD context, the RIB is the control-plane of the routing table (route_ctl.c, route.c, ...).

For routing lookups, though, we can't simply call rib_lookup_*() or rnh->rnh_lookup(), because that is too slow for the data plane.
We need a summarized table for that (e.g. Cisco has CEF for this).
This is where the FIB helps: we can use a radix[4|6] tree and hook into control-plane changes (route_subscription, from fib_algo to route_ctl.c) to build a simple, fast table used only for data-plane lookups.

But we may want to trade speed against memory depending on the number of routes, so we need to be able to change the lookup algorithm.
That is the fib_algo framework: an abstraction layer for FIB lookup algorithms.
Modules like dxr (sys/netinet/in_fib_dxr.c) can then ignore nhop referencing and control-plane (RIB) subscriptions and focus on implementing their own lookup algorithms.

Now that the relationship is clear, let's look at the problem.

The fib_algo framework is responsible for managing nhop references.
To keep it simple and fast, it allocates an initial array of 16 nhops and doubles it every time that limit is reached.
The array is fast because it is indexed directly by the nhop id (nhop_var.h: struct nhop_priv->nh_idx), on the assumption that the id is unique.
That assumption holds, because both nhop and nhgrp get their indexes from their nh_control, and nh_control guarantees uniqueness. nh_control is per family, just like the fib_algo instances.

When a new route is added, the nhop allocation takes its family from rib_family (nhop_ctl.c:710) and stores it in nh_priv->nh_upper_family.
However, the hash table and index management for a nexthop use nh_neigh_family, which can differ from the upper family (nhop.c:338).

That is what breaks the assumption. As today IPv4 over IPv6 nexthop.

If the bug is triggered in a production environment, what is the likely outcome?

Technically it's an index corruption.

  • sys/net/route/fib_algo.c:1110 (free based on idx, invalid refcount, you know the rest)
  • sys/net/route/fib_algo.c:1754 (returns a completely wrong nexthop from fib_lookup(), which usually results in dropped packets)
  • sys/net/route/fib_algo.c:1784 (refcounts the wrong nexthop, you know the rest)
  • sys/net/route/fib_algo.c:1845 ( this is where the KASSERT fires, unreferencing the wrong nexthop)

Hope it helps the reviewers.

sys/net/route/fib_algo.c
151
948

Where is this flag used?

1853

Same for other assertions.

1886

Shouldn't the assertion be fd->fd_num_af < FD_MAX_NH_AF?

pouria marked 3 inline comments as done.

Address @markj comments.

sys/net/route/fib_algo.c
948

leftover of my previous design when I didn't have fd_num_af. Removed.

1886

No, it's intentionally starts from 1. See line 920.

sys/net/route/fib_algo.c
1886

But the fd_af array has FD_MAX_NH_AF elements, so if fd->fd_num_af == FD_MAX_NH_AF here then the accesses below are out of bounds. Presumably the array size should be FD_MAX_NH_AF + 1 then.

pouria marked 2 inline comments as done.

Address @markj comment. You're right, sorry about that