/*
 * 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.
 */

#ifndef __FIB_SOURCE_H__
#define __FIB_SOURCE_H__

#include <vnet/vnet.h>

/**
 * The different sources that can create a route.
 * The sources are defined here with their relative priority order.
 * The lower the value the higher the priority
 */
typedef enum fib_source_t_ {
    /**
     * An invalid source
     * This is not a real source, so don't use it to source a prefix.
     * It exists here to provide a value for inexistant/uninitialized source
     */
    FIB_SOURCE_INVALID = 0,
    /**
     * Marker. Add new values after this one.
     */
    FIB_SOURCE_FIRST,
    /**
     * Special sources. These are for entries that are added to all
     * FIBs by default, and should never be over-ridden (hence they
     * are the highest priority)
     */
    FIB_SOURCE_SPECIAL = FIB_SOURCE_FIRST,
    /**
     * Classify. A route that links directly to a classify adj
     */
    FIB_SOURCE_CLASSIFY,
    /**
     * A route the is being 'proxied' on behalf of another device
     */
    FIB_SOURCE_PROXY,
    /**
     * Route added as a result of interface configuration.
     * this will also come from the API/CLI, but the distinction is
     * that is from confiiguration on an interface, not a 'ip route' command
     */
    FIB_SOURCE_INTERFACE,
    /**
     * SRv6 and SR-MPLS
     */
    FIB_SOURCE_SR,
    /**
     * From the BIER subsystem
     */
    FIB_SOURCE_BIER,
    /**
     * From 6RD.
     */
    FIB_SOURCE_6RD,
    /**
     * From the control plane API
     */
    FIB_SOURCE_API,
    /**
     * From the CLI.
     */
    FIB_SOURCE_CLI,
    /**
     * LISP
     */
    FIB_SOURCE_LISP,
    /**
     * IPv[46] Mapping
     */
    FIB_SOURCE_MAP,
    /**
     * DHCP
     */
    FIB_SOURCE_DHCP,
    /**
     * IPv6 Proxy ND
     */
    FIB_SOURCE_IP6_ND_PROXY,
    /**
     * IPv6 ND (seen in the link-local tables)
     */
    FIB_SOURCE_IP6_ND,
    /**
     * Adjacency source.
     * routes created as a result of ARP/ND entries. This is lower priority
     * then the API/CLI. This is on purpose. trust me.
     */
    FIB_SOURCE_ADJ,
    /**
     * MPLS label. The prefix has been assigned a local label. This source
     * never provides forwarding information, instead it acts as a place-holder
     * so the association of label to prefix can be maintained
     */
    FIB_SOURCE_MPLS,
    /**
     * Attached Export source.
     * routes created as a result of attahced export. routes thus sourced
     * will be present in the export tables
     */
    FIB_SOURCE_AE,
    /**
     * Recursive resolution source.
     * Used to install an entry that is the resolution traget of another.
     */
    FIB_SOURCE_RR,
    /**
     * uRPF bypass/exemption.
     * Used to install an entry that is exempt from the loose uRPF check
     */
    FIB_SOURCE_URPF_EXEMPT,
    /**
     * The default route source.
     * The default route is always added to the FIB table (like the
     * special sources) but we need to be able to over-ride it with
     * 'ip route' sources when provided
     */
    FIB_SOURCE_DEFAULT_ROUTE,
    /**
     * The interpose source.
     * This is not a real source, so don't use it to source a prefix.
     * It exists here to provide a value against which to register to the
     * VFT for providing the interpose actions to a real source.
     */
    FIB_SOURCE_INTERPOSE,
    /**
     * Marker. add new entries before this one.
     */
    FIB_SOURCE_LAST = FIB_SOURCE_INTERPOSE,
} __attribute__ ((packed)) fib_source_t;

STATIC_ASSERT (sizeof(fib_source_t) == 1,
	       "FIB too many sources");

#define FIB_SOURCES {					\
    [FIB_SOURCE_INVALID] = "invalid",			\
    [FIB_SOURCE_SPECIAL] = "special",			\
    [FIB_SOURCE_INTERFACE] = "interface",		\
    [FIB_SOURCE_PROXY] = "proxy",                       \
    [FIB_SOURCE_BIER] = "BIER",			        \
    [FIB_SOURCE_6RD] = "6RD",			        \
    [FIB_SOURCE_API] = "API",			        \
    [FIB_SOURCE_CLI] = "CLI",			        \
    [FIB_SOURCE_ADJ] = "adjacency",			\
    [FIB_SOURCE_MAP] = "MAP",			        \
    [FIB_SOURCE_SR] = "SR",			        \
    [FIB_SOURCE_LISP] = "LISP", 			\
    [FIB_SOURCE_CLASSIFY] = "classify",			\
    [FIB_SOURCE_DHCP] = "DHCP",   			\
    [FIB_SOURCE_IP6_ND_PROXY] = "IPv6-proxy-nd",        \
    [FIB_SOURCE_IP6_ND] = "IPv6-nd",                    \
    [FIB_SOURCE_RR] = "recursive-resolution",	        \
    [FIB_SOURCE_AE] = "attached_export",	        \
    [FIB_SOURCE_MPLS] = "mpls",           	        \
    [FIB_SOURCE_URPF_EXEMPT] = "urpf-exempt",	        \
    [FIB_SOURCE_DEFAULT_ROUTE] = "default-route",	\
    [FIB_SOURCE_INTERPOSE] = "interpose",               \
}

/**
 * Each source is assigned a priority. lower priority is beeter.
 * the source with the best source with have its contribution added
 * to forwarding. the lesser sources will be 'remembered' by FIB and
 * added to forwarding should the best source be removed.
 */
typedef u8 fib_source_priority_t;

/**
 * source comparison
 */
typedef enum fib_source_priority_cmp_t_
{
    FIB_SOURCE_CMP_BETTER,
    FIB_SOURCE_CMP_WORSE,
    FIB_SOURCE_CMP_EQUAL,
} fib_source_priority_cmp_t;

/**
 * Each source has a defined behaviour that controls how entries
 * behave that have that source
 */
typedef enum fib_source_behaviour_t_
{
    /**
     * If your adding a new source from a plugin pick one of these
     */
    /** Default behaviour - always install a drop */
    FIB_SOURCE_BH_DROP,
    /** add paths with [mpls] path extensions */
    FIB_SOURCE_BH_API,
    /** add paths without path extensions */
    FIB_SOURCE_BH_SIMPLE,

    /**
     * If your adding a new source from a plugin
     * these are probably not the behaviour you're lokking for.
     */
    /** recursive resolution w/ cover tracking*/
    FIB_SOURCE_BH_RR,
    /** associated label stored in private data */
    FIB_SOURCE_BH_MPLS,
    /** cover tracking w/ glean management */
    FIB_SOURCE_BH_INTERFACE,
    /** interpose */
    FIB_SOURCE_BH_INTERPOSE,
    /** simple + source fib tracking */
    FIB_SOURCE_BH_LISP,
    /** adj w/ cover tracking + refinement */
    FIB_SOURCE_BH_ADJ,
} fib_source_behaviour_t;

#define FIB_SOURCE_BH_MAX (FIB_SOURCE_BH_ADJ+1)

#define FIB_SOURCE_BEHAVIOURS {                 \
    [FIB_SOURCE_BH_DROP] = "drop",		\
    [FIB_SOURCE_BH_RR] = "rr",                  \
    [FIB_SOURCE_BH_MPLS] = "mpls",              \
    [FIB_SOURCE_BH_INTERFACE] = "interface",    \
    [FIB_SOURCE_BH_INTERPOSE] = "interpose",    \
    [FIB_SOURCE_BH_LISP] = "lisp",              \
    [FIB_SOURCE_BH_ADJ] = "adjacency",          \
    [FIB_SOURCE_BH_API] = "api",                \
    [FIB_SOURCE_BH_SIMPLE] = "simple",          \
}

/**
 * The fixed source to priority mappings.
 * Declared here so those adding new sources can better determine their respective
 * priority values.
 */
#define foreach_fib_source                                      \
    /** you can't do better then the special source */         \
    _(FIB_SOURCE_SPECIAL,       0x00, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_CLASSIFY,      0x01, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_PROXY,         0x02, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_INTERFACE,     0x03, FIB_SOURCE_BH_INTERFACE) \
    _(FIB_SOURCE_SR,            0x10, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_BIER,          0x20, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_6RD,           0x30, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_API,           0x80, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_CLI,           0x81, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_LISP,          0x90, FIB_SOURCE_BH_LISP)      \
    _(FIB_SOURCE_MAP,           0xa0, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_DHCP,          0xb0, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_IP6_ND_PROXY,  0xc0, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_IP6_ND,        0xc1, FIB_SOURCE_BH_API)       \
    _(FIB_SOURCE_ADJ,           0xd0, FIB_SOURCE_BH_ADJ)       \
    _(FIB_SOURCE_MPLS,          0xe0, FIB_SOURCE_BH_MPLS)      \
    _(FIB_SOURCE_AE,            0xf0, FIB_SOURCE_BH_SIMPLE)    \
    _(FIB_SOURCE_RR,            0xfb, FIB_SOURCE_BH_RR)        \
    _(FIB_SOURCE_URPF_EXEMPT,   0xfc, FIB_SOURCE_BH_RR)        \
    _(FIB_SOURCE_DEFAULT_ROUTE, 0xfd, FIB_SOURCE_BH_DROP)      \
    _(FIB_SOURCE_INTERPOSE,     0xfe, FIB_SOURCE_BH_INTERPOSE) \
    _(FIB_SOURCE_INVALID,       0xff, FIB_SOURCE_BH_DROP)

/**
 * Some priority values that plugins might use when they are not to concerned
 * where in the list they'll go.
 */
#define FIB_SOURCE_PRIORITY_HI 0x10
#define FIB_SOURCE_PRIORITY_LOW 0xd0


extern u16 fib_source_get_prio(fib_source_t src);
extern fib_source_behaviour_t fib_source_get_behaviour(fib_source_t src);
extern fib_source_priority_cmp_t fib_source_cmp(fib_source_t s1,
                                                fib_source_t s2);

extern u8 *format_fib_source(u8 *s, va_list *a);

extern fib_source_t fib_source_allocate(const char *name,
                                        fib_source_priority_t prio,
                                        fib_source_behaviour_t bh);

extern void fib_source_register(fib_source_t src,
                                fib_source_priority_t prio,
                                fib_source_behaviour_t bh);

typedef walk_rc_t (*fib_source_walk_t)(fib_source_t id,
                                       const char *name,
                                       fib_source_priority_t prio,
                                       fib_source_behaviour_t bh,
                                       void *ctx);
extern void fib_source_walk(fib_source_walk_t fn,
                            void *ctx);

extern void fib_source_module_init(void);

#endif