summaryrefslogtreecommitdiffstats
path: root/src/vnet/fib/fib_entry_src_interface.c
blob: e1725773d93678ebedcdbb2949a448918d3bfc21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * 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 "fib_entry.h"
#include "fib_entry_src.h"
#include "fib_path_list.h"
#include "fib_internal.h"
#include "fib_table.h"
#include "fib_entry_cover.h"
#include "fib_attached_export.h"

/**
 * Source initialisation Function 
 */
static void
fib_entry_src_interface_init (fib_entry_src_t *src)
{
    src->u.interface.fesi_cover = FIB_NODE_INDEX_INVALID;
    src->u.interface.fesi_sibling = FIB_NODE_INDEX_INVALID;
}

static void
fib_entry_src_interface_add (fib_entry_src_t *src,
                             const fib_entry_t *entry,
                             fib_entry_flag_t flags,
                             dpo_proto_t proto,
                             const dpo_id_t *dpo)
{
    src->fes_pl = fib_path_list_create_special(
                      proto,
                      fib_entry_src_flags_2_path_list_flags(flags),
                      dpo);
}

static void
fib_entry_src_interface_remove (fib_entry_src_t *src)
{
    src->fes_pl = FIB_NODE_INDEX_INVALID;
}

static void
fib_entry_src_interface_path_swap (fib_entry_src_t *src,
				   const fib_entry_t *entry,
				   fib_path_list_flags_t pl_flags,
				   const fib_route_path_t *paths)
{
    fib_node_index_t fib_entry_index;
    ip_adjacency_t *adj;

    fib_entry_index = fib_entry_get_index(entry);
    src->fes_pl = fib_path_list_create(pl_flags, paths);

    /*
     * this is a hack to get the entry's prefix into the glean adjacency
     * so that it is available for fast retrieval in the switch path.
     */
    if (!(FIB_ENTRY_FLAG_LOCAL & src->fes_entry_flags))
    {
        adj_index_t ai;

        ai = fib_path_list_get_adj(src->fes_pl,
                                   fib_entry_get_default_chain_type(
                                       fib_entry_get(fib_entry_index)));
        if (INDEX_INVALID != ai)
        {
            adj = adj_get(ai);

            if (IP_LOOKUP_NEXT_GLEAN == adj->lookup_next_index)
            {
                /*
                 * the connected prefix will link to a glean on a non-p2p
                 * u.interface.
                 */
                adj->sub_type.glean.receive_addr = entry->fe_prefix.fp_addr;
            }
        }
    }
}

/*
 * Source activate. 
 * Called when the source is teh new longer best source on the entry
 */
static int
fib_entry_src_interface_activate (fib_entry_src_t *src,
				  const fib_entry_t *fib_entry)
{
    fib_entry_t *cover;

    if (FIB_ENTRY_FLAG_LOCAL & src->fes_entry_flags)
    {
	/*
	 * Track the covering attached/connected cover. This is so that
	 * during an attached export of the cover, this local prefix is
	 * also exported
	 */
	src->u.interface.fesi_cover =
	    fib_table_get_less_specific(fib_entry->fe_fib_index,
					&fib_entry->fe_prefix);

	ASSERT(FIB_NODE_INDEX_INVALID != src->u.interface.fesi_cover);

	cover = fib_entry_get(src->u.interface.fesi_cover);

	src->u.interface.fesi_sibling =
	    fib_entry_cover_track(cover, fib_entry_get_index(fib_entry));
    }

    return (!0);
}


/*
 * Source Deactivate. 
 * Called when the source is no longer best source on the entry
 */
static void
fib_entry_src_interface_deactivate (fib_entry_src_t *src,
				    const fib_entry_t *fib_entry)
{
    fib_entry_t *cover;

    /*
     * remove the dependency on the covering entry
     */
    if (FIB_NODE_INDEX_INVALID != src->u.interface.fesi_cover)
    {
	cover = fib_entry_get(src->u.interface.fesi_cover);

	fib_entry_cover_untrack(cover, src->u.interface.fesi_sibling);

	src->u.interface.fesi_cover = FIB_NODE_INDEX_INVALID;
    }
}

static fib_entry_src_cover_res_t
fib_entry_src_interface_cover_change (fib_entry_src_t *src,
				      const fib_entry_t *fib_entry)
{
    fib_entry_src_cover_res_t res = {
	.install = !0,
	.bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
    };

    if (FIB_NODE_INDEX_INVALID == src->u.interface.fesi_cover)
    {
	/*
	 * not tracking the cover. surprised we got poked?
	 */
	return (res);
    }

    /*
     * this function is called when this entry's cover has a more specific
     * entry inserted benaeth it. That does not necessarily mean that this
     * entry is covered by the new prefix. check that
     */
    if (src->u.interface.fesi_cover !=
        fib_table_get_less_specific(fib_entry->fe_fib_index,
                                    &fib_entry->fe_prefix))
    {
	fib_entry_src_interface_deactivate(src, fib_entry);
	fib_entry_src_interface_activate(src, fib_entry);
    }
    return (res);
}

static void
fib_entry_src_interface_installed (fib_entry_src_t *src,
				   const fib_entry_t *fib_entry)
{
    /*
     * The interface source now rules! poke our cover to get exported
     */
    fib_entry_t *cover;

    if (FIB_NODE_INDEX_INVALID != src->u.interface.fesi_cover)
    {
	cover = fib_entry_get(src->u.interface.fesi_cover);

	fib_attached_export_covered_added(cover,
					  fib_entry_get_index(fib_entry));
    }
}

static u8*
fib_entry_src_interface_format (fib_entry_src_t *src,
				u8* s)
{
    return (format(s, " cover:%d", src->u.interface.fesi_cover));
}

const static fib_entry_src_vft_t interface_src_vft = {
    .fesv_init = fib_entry_src_interface_init,
    .fesv_add = fib_entry_src_interface_add,
    .fesv_remove = fib_entry_src_interface_remove,
    .fesv_path_swap = fib_entry_src_interface_path_swap,
    .fesv_activate = fib_entry_src_interface_activate,
    .fesv_deactivate = fib_entry_src_interface_deactivate,
    .fesv_format = fib_entry_src_interface_format,
    .fesv_installed = fib_entry_src_interface_installed,
    .fesv_cover_change = fib_entry_src_interface_cover_change,
    /*
     * not concerned about updates to the cover. the cover will
     * decide to export or not
     */
};

void
fib_entry_src_interface_register (void)
{
    fib_entry_src_behaviour_register(FIB_SOURCE_BH_INTERFACE,
                                     &interface_src_vft);
}
an class="o">= 0; i < vec_len (handles); i++) { a->app_index = app->api_client_index; a->handle = handles[i]; /* seg manager is removed when unbind completes */ vnet_unbind (a); } /* * Free the event fifo in the /vpe-api shared-memory segment */ oldheap = svm_push_data_heap (am->vlib_rp); if (app->event_queue) unix_shared_memory_queue_free (app->event_queue); svm_pop_heap (oldheap); application_table_del (app); pool_put (app_pool, app); } static void application_verify_cb_fns (session_cb_vft_t * cb_fns) { if (cb_fns->session_accept_callback == 0) clib_warning ("No accept callback function provided"); if (cb_fns->session_connected_callback == 0) clib_warning ("No session connected callback function provided"); if (cb_fns->session_disconnect_callback == 0) clib_warning ("No session disconnect callback function provided"); if (cb_fns->session_reset_callback == 0) clib_warning ("No session reset callback function provided"); } int application_init (application_t * app, u32 api_client_index, u64 * options, session_cb_vft_t * cb_fns) { api_main_t *am = &api_main; segment_manager_t *sm; segment_manager_properties_t *props; void *oldheap; u32 app_evt_queue_size; int rv; app_evt_queue_size = options[APP_EVT_QUEUE_SIZE] > 0 ? options[APP_EVT_QUEUE_SIZE] : default_app_evt_queue_size; /* Allocate event fifo in the /vpe-api shared-memory segment */ oldheap = svm_push_data_heap (am->vlib_rp); /* Allocate server event queue */ app->event_queue = unix_shared_memory_queue_init (app_evt_queue_size, sizeof (session_fifo_event_t), 0 /* consumer pid */ , 0 /* (do not) signal when queue non-empty */ ); svm_pop_heap (oldheap); /* Setup segment manager */ sm = segment_manager_new (); sm->app_index = app->index; props = &app->sm_properties; props->add_segment_size = options[SESSION_OPTIONS_ADD_SEGMENT_SIZE]; props->rx_fifo_size = options[SESSION_OPTIONS_RX_FIFO_SIZE]; props->tx_fifo_size = options[SESSION_OPTIONS_TX_FIFO_SIZE]; props->add_segment = props->add_segment_size != 0; if ((rv = segment_manager_init (sm, props, options[SESSION_OPTIONS_SEGMENT_SIZE]))) return rv; app->first_segment_manager = segment_manager_index (sm); app->api_client_index = api_client_index; app->flags = options[SESSION_OPTIONS_FLAGS]; app->cb_fns = *cb_fns; /* Check that the obvious things are properly set up */ application_verify_cb_fns (cb_fns); /* Add app to lookup by api_client_index table */ application_table_add (app); return 0; } application_t * application_get (u32 index) { return pool_elt_at_index (app_pool, index); } application_t * application_get_if_valid (u32 index) { if (pool_is_free_index (app_pool, index)) return 0; return pool_elt_at_index (app_pool, index); } u32 application_get_index (application_t * app) { return app - app_pool; } static segment_manager_t * application_alloc_segment_manager (application_t * app) { segment_manager_t *sm = 0; if (app->first_segment_manager != (u32) ~ 0) { sm = segment_manager_get (app->first_segment_manager); app->first_segment_manager = ~0; return sm; } sm = segment_manager_new (); if (segment_manager_init (sm, &app->sm_properties, 0)) return 0; return sm; } /** * Start listening local transport endpoint for requested transport. * * Creates a 'dummy' stream session with state LISTENING to be used in session * lookups, prior to establishing connection. Requests transport to build * it's own specific listening connection. */ int application_start_listen (application_t * srv, session_type_t session_type, transport_endpoint_t * tep, u64 * res) { segment_manager_t *sm; stream_session_t *s; u64 handle; s = listen_session_new (session_type); s->app_index = srv->index; if (stream_session_listen (s, tep)) goto err; /* Allocate segment manager. All sessions derived out of a listen session * have fifos allocated by the same segment manager. */ sm = application_alloc_segment_manager (srv); if (sm == 0) goto err; /* Add to app's listener table. Useful to find all child listeners * when app goes down, although, just for unbinding this is not needed */ handle = listen_session_get_handle (s); hash_set (srv->listeners_table, handle, segment_manager_index (sm)); *res = handle; return 0; err: listen_session_del (s); return -1; } /** * Stop listening on session associated to handle */ int application_stop_listen (application_t * srv, u64 handle) { stream_session_t *listener; uword *indexp; segment_manager_t *sm; if (srv && hash_get (srv->listeners_table, handle) == 0) { clib_warning ("app doesn't own handle %llu!", handle); return -1; } listener = listen_session_get_from_handle (handle); stream_session_stop_listen (listener); indexp = hash_get (srv->listeners_table, handle); ASSERT (indexp); sm = segment_manager_get (*indexp); segment_manager_del (sm); hash_unset (srv->listeners_table, handle); listen_session_del (listener); return 0; } int application_open_session (application_t * app, session_type_t sst, transport_endpoint_t * tep, u32 api_context) { segment_manager_t *sm; transport_connection_t *tc = 0; int rv; /* Make sure we have a segment manager for connects */ if (app->connects_seg_manager == (u32) ~ 0) { sm = application_alloc_segment_manager (app); if (sm == 0) return -1; app->connects_seg_manager = segment_manager_index (sm); } if ((rv = stream_session_open (app->index, sst, tep, &tc))) return rv; /* Store api_context for when the reply comes. Not the nicest thing * but better allocating a separate half-open pool. */ tc->s_index = api_context; return 0; } segment_manager_t * application_get_connect_segment_manager (application_t * app) { ASSERT (app->connects_seg_manager != (u32) ~ 0); return segment_manager_get (app->connects_seg_manager); } segment_manager_t * application_get_listen_segment_manager (application_t * app, stream_session_t * s) { uword *smp; smp = hash_get (app->listeners_table, listen_session_get_handle (s)); ASSERT (smp != 0); return segment_manager_get (*smp); } static u8 * app_get_name_from_reg_index (application_t * app) { u8 *app_name; vl_api_registration_t *regp; regp = vl_api_client_index_to_registration (app->api_client_index); if (!regp) app_name = format (0, "builtin-%d%c", app->index, 0); else app_name = format (0, "%s%c", regp->name, 0); return app_name; } u8 * format_application_listener (u8 * s, va_list * args) { application_t *app = va_arg (*args, application_t *); u64 handle = va_arg (*args, u64); u32 index = va_arg (*args, u32); int verbose = va_arg (*args, int); stream_session_t *listener; u8 *app_name, *str; if (app == 0) { if (verbose) s = format (s, "%-40s%-20s%-15s%-15s%-10s", "Connection", "App", "API Client", "ListenerID", "SegManager"); else s = format (s, "%-40s%-20s", "Connection", "App"); return s; } app_name = app_get_name_from_reg_index (app); listener = listen_session_get_from_handle (handle); str = format (0, "%U", format_stream_session, listener, verbose); if (verbose) { s = format (s, "%-40s%-20s%-15u%-15u%-10u", str, app_name, app->api_client_index, handle, index); } else s = format (s, "%-40s%-20s", str, app_name); vec_free (app_name); return s; } void application_format_connects (application_t * app, int verbose) { vlib_main_t *vm = vlib_get_main (); segment_manager_t *sm; u8 *app_name, *s = 0; int i, j; /* Header */ if (app == 0) { if (verbose) vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App", "API Client", "SegManager"); else vlib_cli_output (vm, "%-40s%-20s", "Connection", "App"); return; } /* make sure */ if (app->connects_seg_manager == (u32) ~ 0) return; app_name = app_get_name_from_reg_index (app); /* Across all fifo segments */ sm = segment_manager_get (app->connects_seg_manager); for (j = 0; j < vec_len (sm->segment_indices); j++) { svm_fifo_segment_private_t *fifo_segment; svm_fifo_t **fifos; u8 *str; fifo_segment = svm_fifo_get_segment (sm->segment_indices[j]); fifos = svm_fifo_segment_get_fifos (fifo_segment); for (i = 0; i < vec_len (fifos); i++) { svm_fifo_t *fifo; u32 session_index, thread_index; stream_session_t *session; /* There are 2 fifos/session. Avoid printing twice. */ if (i % 2) continue; fifo = fifos[i]; session_index = fifo->server_session_index; thread_index = fifo->server_thread_index; session = stream_session_get (session_index, thread_index); str = format (0, "%U", format_stream_session, session, verbose); if (verbose) s = format (s, "%-40s%-20s%-15u%-10u", str, app_name, app->api_client_index, app->connects_seg_manager); else s = format (s, "%-40s%-20s", str, app_name); vlib_cli_output (vm, "%v", s); vec_reset_length (s); vec_free (str); } vec_free (s); } vec_free (app_name); } u8 * format_application (u8 * s, va_list * args) { application_t *app = va_arg (*args, application_t *); CLIB_UNUSED (int verbose) = va_arg (*args, int); u8 *app_name; if (app == 0) { if (verbose) s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s", "Index", "Name", "API Client", "Add seg size", "Rx fifo size", "Tx fifo size"); else s = format (s, "%-10s%-20s%-20s", "Index", "Name", "API Client"); return s; } app_name = app_get_name_from_reg_index (app); if (verbose) s = format (s, "%-10d%-20s%-15d%-15d%-15d%-15d", app->index, app_name, app->api_client_index, app->sm_properties.add_segment_size, app->sm_properties.rx_fifo_size, app->sm_properties.tx_fifo_size); else s = format (s, "%-10d%-20s%-20d", app->index, app_name, app->api_client_index); return s; } static clib_error_t * show_app_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { application_t *app; int do_server = 0; int do_client = 0; int verbose = 0; if (!session_manager_is_enabled ()) { clib_error_return (0, "session layer is not enabled"); } while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "server")) do_server = 1; else if (unformat (input, "client")) do_client = 1; else if (unformat (input, "verbose")) verbose = 1; else break; } if (do_server) { u64 handle; u32 index; if (pool_elts (app_pool)) { vlib_cli_output (vm, "%U", format_application_listener, 0 /* header */ , 0, 0, verbose); /* *INDENT-OFF* */ pool_foreach (app, app_pool, ({ /* App's listener sessions */ if (hash_elts (app->listeners_table) == 0) continue; hash_foreach (handle, index, app->listeners_table, ({ vlib_cli_output (vm, "%U", format_application_listener, app, handle, index, verbose); })); })); /* *INDENT-ON* */ } else vlib_cli_output (vm, "No active server bindings"); } if (do_client) { if (pool_elts (app_pool)) { application_format_connects (0, verbose); /* *INDENT-OFF* */ pool_foreach (app, app_pool, ({ if (app->connects_seg_manager == (u32)~0) continue; application_format_connects (app, verbose); })); /* *INDENT-ON* */ } else vlib_cli_output (vm, "No active client bindings"); } /* Print app related info */ if (!do_server && !do_client) { vlib_cli_output (vm, "%U", format_application, 0, verbose); pool_foreach (app, app_pool, ( { vlib_cli_output (vm, "%U", format_application, app, verbose); } )); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_app_command, static) = { .path = "show app", .short_help = "show app [server|client] [verbose]", .function = show_app_command_fn, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */