blob: 6db1ca963002f7cfa0eaa80dd5a8ff8f78eff6fc [file] [log] [blame]
/*
* Copyright (C) 2003 Yasuhiro Ohara
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
#include "log.h"
#include "memory.h"
#include "prefix.h"
#include "command.h"
#include "vty.h"
#include "routemap.h"
#include "table.h"
#include "plist.h"
#include "thread.h"
#include "linklist.h"
#include "ospf6_proto.h"
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
#include "ospf6_route.h"
#include "ospf6_zebra.h"
#include "ospf6_message.h"
#include "ospf6_top.h"
#include "ospf6_area.h"
#include "ospf6_interface.h"
#include "ospf6_neighbor.h"
#include "ospf6_asbr.h"
#include "ospf6_intra.h"
#include "ospf6_flood.h"
#include "ospf6d.h"
unsigned char conf_debug_ospf6_asbr = 0;
#define ZROUTE_NAME(x) zebra_route_string(x)
/* AS External LSA origination */
static void
ospf6_as_external_lsa_originate (struct ospf6_route *route)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
struct ospf6_lsa *lsa;
struct ospf6_external_info *info = route->route_option;
struct ospf6_as_external_lsa *as_external_lsa;
char buf[64];
caddr_t p;
if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE (AS_EXTERNAL))
{
prefix2str (&route->prefix, buf, sizeof (buf));
zlog_debug ("Originate AS-External-LSA for %s", buf);
}
/* prepare buffer */
memset (buffer, 0, sizeof (buffer));
lsa_header = (struct ospf6_lsa_header *) buffer;
as_external_lsa = (struct ospf6_as_external_lsa *)
((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header));
p = (caddr_t)
((caddr_t) as_external_lsa + sizeof (struct ospf6_as_external_lsa));
/* Fill AS-External-LSA */
/* Metric type */
if (route->path.metric_type == 2)
SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
else
UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
/* forwarding address */
if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding))
SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
else
UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
/* external route tag */
UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
/* Set metric */
OSPF6_ASBR_METRIC_SET (as_external_lsa, route->path.cost);
/* prefixlen */
as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
/* PrefixOptions */
as_external_lsa->prefix.prefix_options = route->path.prefix_options;
/* don't use refer LS-type */
as_external_lsa->prefix.prefix_refer_lstype = htons (0);
/* set Prefix */
memcpy (p, &route->prefix.u.prefix6,
OSPF6_PREFIX_SPACE (route->prefix.prefixlen));
ospf6_prefix_apply_mask (&as_external_lsa->prefix);
p += OSPF6_PREFIX_SPACE (route->prefix.prefixlen);
/* Forwarding address */
if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F))
{
memcpy (p, &info->forwarding, sizeof (struct in6_addr));
p += sizeof (struct in6_addr);
}
/* External Route Tag */
if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T))
{
/* xxx */
}
/* Fill LSA Header */
lsa_header->age = 0;
lsa_header->type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
lsa_header->id = route->path.origin.id;
lsa_header->adv_router = ospf6->router_id;
lsa_header->seqnum =
ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id,
lsa_header->adv_router, ospf6->lsdb);
lsa_header->length = htons ((caddr_t) p - (caddr_t) lsa_header);
/* LSA checksum */
ospf6_lsa_checksum (lsa_header);
/* create LSA */
lsa = ospf6_lsa_create (lsa_header);
/* Originate */
ospf6_lsa_originate_process (lsa, ospf6);
}
void
ospf6_asbr_lsa_add (struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix asbr_id;
struct ospf6_route *asbr_entry, *route;
char buf[64];
int i;
external = (struct ospf6_as_external_lsa *)
OSPF6_LSA_HEADER_END (lsa->header);
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Calculate AS-External route for %s", lsa->name);
if (lsa->header->adv_router == ospf6->router_id)
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Ignore self-originated AS-External-LSA");
return;
}
if (OSPF6_ASBR_METRIC (external) == OSPF_LS_INFINITY)
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Ignore LSA with LSInfinity Metric");
return;
}
if (CHECK_FLAG(external->prefix.prefix_options, OSPF6_PREFIX_OPTION_NU))
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Ignore LSA with NU bit set Metric");
return;
}
ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &asbr_id);
asbr_entry = ospf6_route_lookup (&asbr_id, ospf6->brouter_table);
if (asbr_entry == NULL ||
! CHECK_FLAG (asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E))
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
{
prefix2str (&asbr_id, buf, sizeof (buf));
zlog_debug ("ASBR entry not found: %s", buf);
}
return;
}
route = ospf6_route_create ();
route->type = OSPF6_DEST_TYPE_NETWORK;
route->prefix.family = AF_INET6;
route->prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr (&route->prefix.u.prefix6, &external->prefix);
route->path.area_id = asbr_entry->path.area_id;
route->path.origin.type = lsa->header->type;
route->path.origin.id = lsa->header->id;
route->path.origin.adv_router = lsa->header->adv_router;
route->path.prefix_options = external->prefix.prefix_options;
if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E))
{
route->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
route->path.metric_type = 2;
route->path.cost = asbr_entry->path.cost;
route->path.cost_e2 = OSPF6_ASBR_METRIC (external);
}
else
{
route->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
route->path.metric_type = 1;
route->path.cost = asbr_entry->path.cost + OSPF6_ASBR_METRIC (external);
route->path.cost_e2 = 0;
}
for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
ospf6_nexthop_copy (&route->nexthop[i], &asbr_entry->nexthop[i]);
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
{
prefix2str (&route->prefix, buf, sizeof (buf));
zlog_debug ("AS-External route add: %s", buf);
}
ospf6_route_add (route, ospf6->route_table);
}
void
ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix prefix;
struct ospf6_route *route;
char buf[64];
external = (struct ospf6_as_external_lsa *)
OSPF6_LSA_HEADER_END (lsa->header);
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Withdraw AS-External route for %s", lsa->name);
if (lsa->header->adv_router == ospf6->router_id)
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
zlog_debug ("Ignore self-originated AS-External-LSA");
return;
}
memset (&prefix, 0, sizeof (struct prefix));
prefix.family = AF_INET6;
prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr (&prefix.u.prefix6, &external->prefix);
route = ospf6_route_lookup (&prefix, ospf6->route_table);
if (route == NULL)
{
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
{
prefix2str (&prefix, buf, sizeof (buf));
zlog_debug ("AS-External route %s not found", buf);
}
return;
}
for (ospf6_route_lock (route);
route && ospf6_route_is_prefix (&prefix, route);
route = ospf6_route_next (route))
{
if (route->type != OSPF6_DEST_TYPE_NETWORK)
continue;
if (route->path.origin.type != lsa->header->type)
continue;
if (route->path.origin.id != lsa->header->id)
continue;
if (route->path.origin.adv_router != lsa->header->adv_router)
continue;
if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL))
{
prefix2str (&route->prefix, buf, sizeof (buf));
zlog_debug ("AS-External route remove: %s", buf);
}
ospf6_route_remove (route, ospf6->route_table);
}
if (route != NULL)
ospf6_route_unlock (route);
}
void
ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry)
{
struct ospf6_lsa *lsa;
u_int16_t type;
u_int32_t router;
if (! CHECK_FLAG (asbr_entry->flag, OSPF6_ROUTE_BEST))
{
char buf[16];
inet_ntop (AF_INET, &ADV_ROUTER_IN_PREFIX (&asbr_entry->prefix),
buf, sizeof (buf));
zlog_info ("ignore non-best path: lsentry %s add", buf);
return;
}
type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb); lsa;
lsa = ospf6_lsdb_type_router_next (type, router, lsa))
{
if (! OSPF6_LSA_IS_MAXAGE (lsa))
ospf6_asbr_lsa_add (lsa);
}
}
void
ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry)
{
struct ospf6_lsa *lsa;
u_int16_t type;
u_int32_t router;
type = htons (OSPF6_LSTYPE_AS_EXTERNAL);
router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix);
for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb);
lsa; lsa = ospf6_lsdb_type_router_next (type, router, lsa))
ospf6_asbr_lsa_remove (lsa);
}
/* redistribute function */
static void
ospf6_asbr_routemap_set (int type, const char *mapname)
{
if (ospf6->rmap[type].name)
free (ospf6->rmap[type].name);
ospf6->rmap[type].name = strdup (mapname);
ospf6->rmap[type].map = route_map_lookup_by_name (mapname);
}
static void
ospf6_asbr_routemap_unset (int type)
{
if (ospf6->rmap[type].name)
free (ospf6->rmap[type].name);
ospf6->rmap[type].name = NULL;
ospf6->rmap[type].map = NULL;
}
static void
ospf6_asbr_routemap_update (const char *mapname)
{
int type;
if (ospf6 == NULL)
return;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
{
if (ospf6->rmap[type].name)
ospf6->rmap[type].map =
route_map_lookup_by_name (ospf6->rmap[type].name);
else
ospf6->rmap[type].map = NULL;
}
}
int
ospf6_asbr_is_asbr (struct ospf6 *o)
{
return o->external_table->count;
}
static void
ospf6_asbr_redistribute_set (int type)
{
ospf6_zebra_redistribute (type);
}
static void
ospf6_asbr_redistribute_unset (int type)
{
struct ospf6_route *route;
struct ospf6_external_info *info;
ospf6_zebra_no_redistribute (type);
for (route = ospf6_route_head (ospf6->external_table); route;
route = ospf6_route_next (route))
{
info = route->route_option;
if (info->type != type)
continue;
ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex,
&route->prefix);
}
ospf6_asbr_routemap_unset (type);
}
void
ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix,
u_int nexthop_num, struct in6_addr *nexthop)
{
int ret;
struct ospf6_route troute;
struct ospf6_external_info tinfo;
struct ospf6_route *route, *match;
struct ospf6_external_info *info;
struct prefix prefix_id;
struct route_node *node;
char pbuf[64], ibuf[16];
struct listnode *lnode, *lnnode;
struct ospf6_area *oa;
if (! ospf6_zebra_is_redistribute (type))
return;
if (IS_OSPF6_DEBUG_ASBR)
{
prefix2str (prefix, pbuf, sizeof (pbuf));
zlog_debug ("Redistribute %s (%s)", pbuf, ZROUTE_NAME (type));
}
/* if route-map was specified but not found, do not advertise */
if (ospf6->rmap[type].name)
{
if (ospf6->rmap[type].map == NULL)
ospf6_asbr_routemap_update (NULL);
if (ospf6->rmap[type].map == NULL)
{
zlog_warn ("route-map \"%s\" not found, suppress redistributing",
ospf6->rmap[type].name);
return;
}
}
/* apply route-map */
if (ospf6->rmap[type].map)
{
memset (&troute, 0, sizeof (troute));
memset (&tinfo, 0, sizeof (tinfo));
troute.route_option = &tinfo;
tinfo.ifindex = ifindex;
ret = route_map_apply (ospf6->rmap[type].map, prefix,
RMAP_OSPF6, &troute);
if (ret == RMAP_DENYMATCH)
{
if (IS_OSPF6_DEBUG_ASBR)
zlog_debug ("Denied by route-map \"%s\"", ospf6->rmap[type].name);
return;
}
}
match = ospf6_route_lookup (prefix, ospf6->external_table);
if (match)
{
info = match->route_option;
/* copy result of route-map */
if (ospf6->rmap[type].map)
{
if (troute.path.metric_type)
match->path.metric_type = troute.path.metric_type;
if (troute.path.cost)
match->path.cost = troute.path.cost;
if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
memcpy (&info->forwarding, &tinfo.forwarding,
sizeof (struct in6_addr));
}
info->type = type;
match->nexthop[0].ifindex = ifindex;
if (nexthop_num && nexthop)
memcpy (&match->nexthop[0].address, nexthop, sizeof (struct in6_addr));
/* create/update binding in external_id_table */
prefix_id.family = AF_INET;
prefix_id.prefixlen = 32;
prefix_id.u.prefix4.s_addr = htonl (info->id);
node = route_node_get (ospf6->external_id_table, &prefix_id);
node->info = match;
if (IS_OSPF6_DEBUG_ASBR)
{
inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
zlog_debug ("Advertise as AS-External Id:%s", ibuf);
}
match->path.origin.id = htonl (info->id);
ospf6_as_external_lsa_originate (match);
return;
}
/* create new entry */
route = ospf6_route_create ();
route->type = OSPF6_DEST_TYPE_NETWORK;
memcpy (&route->prefix, prefix, sizeof (struct prefix));
info = (struct ospf6_external_info *)
XCALLOC (MTYPE_OSPF6_EXTERNAL_INFO, sizeof (struct ospf6_external_info));
route->route_option = info;
info->id = ospf6->external_id++;
/* copy result of route-map */
if (ospf6->rmap[type].map)
{
if (troute.path.metric_type)
route->path.metric_type = troute.path.metric_type;
if (troute.path.cost)
route->path.cost = troute.path.cost;
if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding))
memcpy (&info->forwarding, &tinfo.forwarding,
sizeof (struct in6_addr));
}
info->type = type;
route->nexthop[0].ifindex = ifindex;
if (nexthop_num && nexthop)
memcpy (&route->nexthop[0].address, nexthop, sizeof (struct in6_addr));
/* create/update binding in external_id_table */
prefix_id.family = AF_INET;
prefix_id.prefixlen = 32;
prefix_id.u.prefix4.s_addr = htonl (info->id);
node = route_node_get (ospf6->external_id_table, &prefix_id);
node->info = route;
route = ospf6_route_add (route, ospf6->external_table);
route->route_option = info;
if (IS_OSPF6_DEBUG_ASBR)
{
inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
zlog_debug ("Advertise as AS-External Id:%s", ibuf);
}
route->path.origin.id = htonl (info->id);
ospf6_as_external_lsa_originate (route);
/* Router-Bit (ASBR Flag) may have to be updated */
for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa))
OSPF6_ROUTER_LSA_SCHEDULE (oa);
}
void
ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex,
struct prefix *prefix)
{
struct ospf6_route *match;
struct ospf6_external_info *info = NULL;
struct route_node *node;
struct ospf6_lsa *lsa;
struct prefix prefix_id;
char pbuf[64], ibuf[16];
struct listnode *lnode, *lnnode;
struct ospf6_area *oa;
match = ospf6_route_lookup (prefix, ospf6->external_table);
if (match == NULL)
{
if (IS_OSPF6_DEBUG_ASBR)
{
prefix2str (prefix, pbuf, sizeof (pbuf));
zlog_debug ("No such route %s to withdraw", pbuf);
}
return;
}
info = match->route_option;
assert (info);
if (info->type != type)
{
if (IS_OSPF6_DEBUG_ASBR)
{
prefix2str (prefix, pbuf, sizeof (pbuf));
zlog_debug ("Original protocol mismatch: %s", pbuf);
}
return;
}
if (IS_OSPF6_DEBUG_ASBR)
{
prefix2str (prefix, pbuf, sizeof (pbuf));
inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf));
zlog_debug ("Withdraw %s (AS-External Id:%s)", pbuf, ibuf);
}
lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_AS_EXTERNAL),
htonl (info->id), ospf6->router_id, ospf6->lsdb);
if (lsa)
ospf6_lsa_purge (lsa);
/* remove binding in external_id_table */
prefix_id.family = AF_INET;
prefix_id.prefixlen = 32;
prefix_id.u.prefix4.s_addr = htonl (info->id);
node = route_node_lookup (ospf6->external_id_table, &prefix_id);
assert (node);
node->info = NULL;
route_unlock_node (node);
ospf6_route_remove (match, ospf6->external_table);
XFREE (MTYPE_OSPF6_EXTERNAL_INFO, info);
/* Router-Bit (ASBR Flag) may have to be updated */
for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa))
OSPF6_ROUTER_LSA_SCHEDULE (oa);
}
DEFUN (ospf6_redistribute,
ospf6_redistribute_cmd,
"redistribute " QUAGGA_REDIST_STR_OSPF6D,
"Redistribute\n"
QUAGGA_REDIST_HELP_STR_OSPF6D
)
{
int type;
type = proto_redistnum(AFI_IP6, argv[0]);
if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
return CMD_WARNING;
ospf6_asbr_redistribute_unset (type);
ospf6_asbr_redistribute_set (type);
return CMD_SUCCESS;
}
DEFUN (ospf6_redistribute_routemap,
ospf6_redistribute_routemap_cmd,
"redistribute " QUAGGA_REDIST_STR_OSPF6D " route-map WORD",
"Redistribute\n"
QUAGGA_REDIST_HELP_STR_OSPF6D
"Route map reference\n"
"Route map name\n"
)
{
int type;
type = proto_redistnum(AFI_IP6, argv[0]);
if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
return CMD_WARNING;
ospf6_asbr_redistribute_unset (type);
ospf6_asbr_routemap_set (type, argv[1]);
ospf6_asbr_redistribute_set (type);
return CMD_SUCCESS;
}
DEFUN (no_ospf6_redistribute,
no_ospf6_redistribute_cmd,
"no redistribute " QUAGGA_REDIST_STR_OSPF6D,
NO_STR
"Redistribute\n"
QUAGGA_REDIST_HELP_STR_OSPF6D
)
{
int type;
type = proto_redistnum(AFI_IP6, argv[0]);
if (type < 0 || type == ZEBRA_ROUTE_OSPF6)
return CMD_WARNING;
ospf6_asbr_redistribute_unset (type);
return CMD_SUCCESS;
}
int
ospf6_redistribute_config_write (struct vty *vty)
{
int type;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
{
if (type == ZEBRA_ROUTE_OSPF6)
continue;
if (! ospf6_zebra_is_redistribute (type))
continue;
if (ospf6->rmap[type].name)
vty_out (vty, " redistribute %s route-map %s%s",
ZROUTE_NAME (type), ospf6->rmap[type].name, VNL);
else
vty_out (vty, " redistribute %s%s",
ZROUTE_NAME (type), VNL);
}
return 0;
}
static void
ospf6_redistribute_show_config (struct vty *vty)
{
int type;
int nroute[ZEBRA_ROUTE_MAX];
int total;
struct ospf6_route *route;
struct ospf6_external_info *info;
total = 0;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
nroute[type] = 0;
for (route = ospf6_route_head (ospf6->external_table); route;
route = ospf6_route_next (route))
{
info = route->route_option;
nroute[info->type]++;
total++;
}
vty_out (vty, "Redistributing External Routes from:%s", VNL);
for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
{
if (type == ZEBRA_ROUTE_OSPF6)
continue;
if (! ospf6_zebra_is_redistribute (type))
continue;
if (ospf6->rmap[type].name)
vty_out (vty, " %d: %s with route-map \"%s\"%s%s", nroute[type],
ZROUTE_NAME (type), ospf6->rmap[type].name,
(ospf6->rmap[type].map ? "" : " (not found !)"),
VNL);
else
vty_out (vty, " %d: %s%s", nroute[type],
ZROUTE_NAME (type), VNL);
}
vty_out (vty, "Total %d routes%s", total, VNL);
}
/* Routemap Functions */
static route_map_result_t
ospf6_routemap_rule_match_address_prefixlist (void *rule,
struct prefix *prefix,
route_map_object_t type,
void *object)
{
struct prefix_list *plist;
if (type != RMAP_OSPF6)
return RMAP_NOMATCH;
plist = prefix_list_lookup (AFI_IP6, (char *) rule);
if (plist == NULL)
return RMAP_NOMATCH;
return (prefix_list_apply (plist, prefix) == PREFIX_DENY ?
RMAP_NOMATCH : RMAP_MATCH);
}
static void *
ospf6_routemap_rule_match_address_prefixlist_compile (const char *arg)
{
return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
}
static void
ospf6_routemap_rule_match_address_prefixlist_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
struct route_map_rule_cmd
ospf6_routemap_rule_match_address_prefixlist_cmd =
{
"ipv6 address prefix-list",
ospf6_routemap_rule_match_address_prefixlist,
ospf6_routemap_rule_match_address_prefixlist_compile,
ospf6_routemap_rule_match_address_prefixlist_free,
};
/* `match interface IFNAME' */
/* Match function should return 1 if match is success else return
zero. */
static route_map_result_t
ospf6_routemap_rule_match_interface (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
struct interface *ifp;
struct ospf6_external_info *ei;
if (type == RMAP_OSPF6)
{
ei = ((struct ospf6_route *) object)->route_option;
ifp = if_lookup_by_name ((char *)rule);
if (ifp != NULL
&& ei->ifindex == ifp->ifindex)
return RMAP_MATCH;
}
return RMAP_NOMATCH;
}
/* Route map `interface' match statement. `arg' should be
interface name. */
static void *
ospf6_routemap_rule_match_interface_compile (const char *arg)
{
return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
}
/* Free route map's compiled `interface' value. */
static void
ospf6_routemap_rule_match_interface_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
/* Route map commands for interface matching. */
struct route_map_rule_cmd
ospf6_routemap_rule_match_interface_cmd =
{
"interface",
ospf6_routemap_rule_match_interface,
ospf6_routemap_rule_match_interface_compile,
ospf6_routemap_rule_match_interface_free
};
static route_map_result_t
ospf6_routemap_rule_set_metric_type (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
char *metric_type = rule;
struct ospf6_route *route = object;
if (type != RMAP_OSPF6)
return RMAP_OKAY;
if (strcmp (metric_type, "type-2") == 0)
route->path.metric_type = 2;
else
route->path.metric_type = 1;
return RMAP_OKAY;
}
static void *
ospf6_routemap_rule_set_metric_type_compile (const char *arg)
{
if (strcmp (arg, "type-2") && strcmp (arg, "type-1"))
return NULL;
return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
}
static void
ospf6_routemap_rule_set_metric_type_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
struct route_map_rule_cmd
ospf6_routemap_rule_set_metric_type_cmd =
{
"metric-type",
ospf6_routemap_rule_set_metric_type,
ospf6_routemap_rule_set_metric_type_compile,
ospf6_routemap_rule_set_metric_type_free,
};
static route_map_result_t
ospf6_routemap_rule_set_metric (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
char *metric = rule;
struct ospf6_route *route = object;
if (type != RMAP_OSPF6)
return RMAP_OKAY;
route->path.cost = atoi (metric);
return RMAP_OKAY;
}
static void *
ospf6_routemap_rule_set_metric_compile (const char *arg)
{
u_int32_t metric;
char *endp;
metric = strtoul (arg, &endp, 0);
if (metric > OSPF_LS_INFINITY || *endp != '\0')
return NULL;
return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
}
static void
ospf6_routemap_rule_set_metric_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
struct route_map_rule_cmd
ospf6_routemap_rule_set_metric_cmd =
{
"metric",
ospf6_routemap_rule_set_metric,
ospf6_routemap_rule_set_metric_compile,
ospf6_routemap_rule_set_metric_free,
};
static route_map_result_t
ospf6_routemap_rule_set_forwarding (void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
char *forwarding = rule;
struct ospf6_route *route = object;
struct ospf6_external_info *info = route->route_option;
if (type != RMAP_OSPF6)
return RMAP_OKAY;
if (inet_pton (AF_INET6, forwarding, &info->forwarding) != 1)
{
memset (&info->forwarding, 0, sizeof (struct in6_addr));
return RMAP_ERROR;
}
return RMAP_OKAY;
}
static void *
ospf6_routemap_rule_set_forwarding_compile (const char *arg)
{
struct in6_addr a;
if (inet_pton (AF_INET6, arg, &a) != 1)
return NULL;
return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
}
static void
ospf6_routemap_rule_set_forwarding_free (void *rule)
{
XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
}
struct route_map_rule_cmd
ospf6_routemap_rule_set_forwarding_cmd =
{
"forwarding-address",
ospf6_routemap_rule_set_forwarding,
ospf6_routemap_rule_set_forwarding_compile,
ospf6_routemap_rule_set_forwarding_free,
};
static int
route_map_command_status (struct vty *vty, int ret)
{
if (! ret)
return CMD_SUCCESS;
switch (ret)
{
case RMAP_RULE_MISSING:
vty_out (vty, "Can't find rule.%s", VNL);
break;
case RMAP_COMPILE_ERROR:
vty_out (vty, "Argument is malformed.%s", VNL);
break;
default:
vty_out (vty, "route-map add set failed.%s", VNL);
break;
}
return CMD_WARNING;
}
/* add "match address" */
DEFUN (ospf6_routemap_match_address_prefixlist,
ospf6_routemap_match_address_prefixlist_cmd,
"match ipv6 address prefix-list WORD",
"Match values\n"
IPV6_STR
"Match address of route\n"
"Match entries of prefix-lists\n"
"IPv6 prefix-list name\n")
{
int ret = route_map_add_match ((struct route_map_index *) vty->index,
"ipv6 address prefix-list", argv[0]);
return route_map_command_status (vty, ret);
}
/* delete "match address" */
DEFUN (ospf6_routemap_no_match_address_prefixlist,
ospf6_routemap_no_match_address_prefixlist_cmd,
"no match ipv6 address prefix-list WORD",
NO_STR
"Match values\n"
IPV6_STR
"Match address of route\n"
"Match entries of prefix-lists\n"
"IPv6 prefix-list name\n")
{
int ret = route_map_delete_match ((struct route_map_index *) vty->index,
"ipv6 address prefix-list", argv[0]);
return route_map_command_status (vty, ret);
}
/* "match interface" */
DEFUN (ospf6_routemap_match_interface,
ospf6_routemap_match_interface_cmd,
"match interface WORD",
MATCH_STR
"Match first hop interface of route\n"
"Interface name\n")
{
return route_map_add_match ((struct route_map_index *) vty->index,
"interface", argv[0]);
}
/* "no match interface WORD" */
DEFUN (ospf6_routemap_no_match_interface,
ospf6_routemap_no_match_interface_cmd,
"no match interface",
MATCH_STR
NO_STR
"Match first hop interface of route\n")
{
int ret = route_map_delete_match ((struct route_map_index *) vty->index,
"interface", (argc == 0) ? NULL : argv[0]);
return route_map_command_status (vty, ret);
}
ALIAS (ospf6_routemap_no_match_interface,
ospf6_routemap_no_match_interface_val_cmd,
"no match interface WORD",
MATCH_STR
NO_STR
"Match first hop interface of route\n"
"Interface name\n")
/* add "set metric-type" */
DEFUN (ospf6_routemap_set_metric_type,
ospf6_routemap_set_metric_type_cmd,
"set metric-type (type-1|type-2)",
"Set value\n"
"Type of metric\n"
"OSPF6 external type 1 metric\n"
"OSPF6 external type 2 metric\n")
{
int ret = route_map_add_set ((struct route_map_index *) vty->index,
"metric-type", argv[0]);
return route_map_command_status (vty, ret);
}
/* delete "set metric-type" */
DEFUN (ospf6_routemap_no_set_metric_type,
ospf6_routemap_no_set_metric_type_cmd,
"no set metric-type (type-1|type-2)",
NO_STR
"Set value\n"
"Type of metric\n"
"OSPF6 external type 1 metric\n"
"OSPF6 external type 2 metric\n")
{
int ret = route_map_delete_set ((struct route_map_index *) vty->index,
"metric-type", argv[0]);
return route_map_command_status (vty, ret);
}
/* add "set metric" */
DEFUN (set_metric,
set_metric_cmd,
"set metric <0-4294967295>",
"Set value\n"
"Metric value\n"
"Metric value\n")
{
int ret = route_map_add_set ((struct route_map_index *) vty->index,
"metric", argv[0]);
return route_map_command_status (vty, ret);
}
/* delete "set metric" */
DEFUN (no_set_metric,
no_set_metric_cmd,
"no set metric",
NO_STR
SET_STR
"Metric value for destination routing protocol\n")
{
int ret = 0;
if (argc == 0)
ret = route_map_delete_set ((struct route_map_index *) vty->index,
"metric", NULL);
else
ret = route_map_delete_set ((struct route_map_index *) vty->index,
"metric", argv[0]);
return route_map_command_status (vty, ret);
}
ALIAS (no_set_metric,
no_set_metric_val_cmd,
"no set metric <0-4294967295>",
NO_STR
SET_STR
"Metric value for destination routing protocol\n"
"Metric value\n")
/* add "set forwarding-address" */
DEFUN (ospf6_routemap_set_forwarding,
ospf6_routemap_set_forwarding_cmd,
"set forwarding-address X:X::X:X",
"Set value\n"
"Forwarding Address\n"
"IPv6 Address\n")
{
int ret = route_map_add_set ((struct route_map_index *) vty->index,
"forwarding-address", argv[0]);
return route_map_command_status (vty, ret);
}
/* delete "set forwarding-address" */
DEFUN (ospf6_routemap_no_set_forwarding,
ospf6_routemap_no_set_forwarding_cmd,
"no set forwarding-address X:X::X:X",
NO_STR
"Set value\n"
"Forwarding Address\n"
"IPv6 Address\n")
{
int ret = route_map_delete_set ((struct route_map_index *) vty->index,
"forwarding-address", argv[0]);
return route_map_command_status (vty, ret);
}
static void
ospf6_routemap_init (void)
{
route_map_init ();
route_map_init_vty ();
route_map_add_hook (ospf6_asbr_routemap_update);
route_map_delete_hook (ospf6_asbr_routemap_update);
route_map_install_match (&ospf6_routemap_rule_match_address_prefixlist_cmd);
route_map_install_match (&ospf6_routemap_rule_match_interface_cmd);
route_map_install_set (&ospf6_routemap_rule_set_metric_type_cmd);
route_map_install_set (&ospf6_routemap_rule_set_metric_cmd);
route_map_install_set (&ospf6_routemap_rule_set_forwarding_cmd);
/* Match address prefix-list */
install_element (RMAP_NODE, &ospf6_routemap_match_address_prefixlist_cmd);
install_element (RMAP_NODE, &ospf6_routemap_no_match_address_prefixlist_cmd);
/* Match interface */
install_element (RMAP_NODE, &ospf6_routemap_match_interface_cmd);
install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_cmd);
install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_val_cmd);
/* ASE Metric Type (e.g. Type-1/Type-2) */
install_element (RMAP_NODE, &ospf6_routemap_set_metric_type_cmd);
install_element (RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd);
/* ASE Metric */
install_element (RMAP_NODE, &set_metric_cmd);
install_element (RMAP_NODE, &no_set_metric_cmd);
install_element (RMAP_NODE, &no_set_metric_val_cmd);
/* ASE Metric */
install_element (RMAP_NODE, &ospf6_routemap_set_forwarding_cmd);
install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd);
}
/* Display functions */
static char *
ospf6_as_external_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf,
int buflen, int pos)
{
struct ospf6_as_external_lsa *external;
struct in6_addr in6;
int prefix_length = 0;
if (lsa)
{
external = (struct ospf6_as_external_lsa *)
OSPF6_LSA_HEADER_END (lsa->header);
if (pos == 0)
{
ospf6_prefix_in6_addr (&in6, &external->prefix);
prefix_length = external->prefix.prefix_length;
}
else {
in6 = *((struct in6_addr *)
((caddr_t) external + sizeof (struct ospf6_as_external_lsa) +
OSPF6_PREFIX_SPACE (external->prefix.prefix_length)));
}
if (buf)
{
inet_ntop (AF_INET6, &in6, buf, buflen);
if (prefix_length)
sprintf (&buf[strlen(buf)], "/%d", prefix_length);
}
}
return (buf);
}
static int
ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
char buf[64];
assert (lsa->header);
external = (struct ospf6_as_external_lsa *)
OSPF6_LSA_HEADER_END (lsa->header);
/* bits */
snprintf (buf, sizeof (buf), "%c%c%c",
(CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E) ? 'E' : '-'),
(CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F) ? 'F' : '-'),
(CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T' : '-'));
vty_out (vty, " Bits: %s%s", buf, VNL);
vty_out (vty, " Metric: %5lu%s", (u_long) OSPF6_ASBR_METRIC (external),
VNL);
ospf6_prefix_options_printbuf (external->prefix.prefix_options,
buf, sizeof (buf));
vty_out (vty, " Prefix Options: %s%s", buf,
VNL);
vty_out (vty, " Referenced LSType: %d%s",
ntohs (external->prefix.prefix_refer_lstype),
VNL);
vty_out (vty, " Prefix: %s%s",
ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 0), VNL);
/* Forwarding-Address */
if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F))
{
vty_out (vty, " Forwarding-Address: %s%s",
ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 1),
VNL);
}
return 0;
}
static void
ospf6_asbr_external_route_show (struct vty *vty, struct ospf6_route *route)
{
struct ospf6_external_info *info = route->route_option;
char prefix[64], id[16], forwarding[64];
u_int32_t tmp_id;
prefix2str (&route->prefix, prefix, sizeof (prefix));
tmp_id = ntohl (info->id);
inet_ntop (AF_INET, &tmp_id, id, sizeof (id));
if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding))
inet_ntop (AF_INET6, &info->forwarding, forwarding, sizeof (forwarding));
else
snprintf (forwarding, sizeof (forwarding), ":: (ifindex %d)",
route->nexthop[0].ifindex);
vty_out (vty, "%c %-32s %-15s type-%d %5lu %s%s",
zebra_route_char(info->type),
prefix, id, route->path.metric_type,
(u_long) (route->path.metric_type == 2 ?
route->path.cost_e2 : route->path.cost),
forwarding, VNL);
}
DEFUN (show_ipv6_ospf6_redistribute,
show_ipv6_ospf6_redistribute_cmd,
"show ipv6 ospf6 redistribute",
SHOW_STR
IP6_STR
OSPF6_STR
"redistributing External information\n"
)
{
struct ospf6_route *route;
ospf6_redistribute_show_config (vty);
for (route = ospf6_route_head (ospf6->external_table); route;
route = ospf6_route_next (route))
ospf6_asbr_external_route_show (vty, route);
return CMD_SUCCESS;
}
struct ospf6_lsa_handler as_external_handler =
{
OSPF6_LSTYPE_AS_EXTERNAL,
"AS-External",
"ASE",
ospf6_as_external_lsa_show,
ospf6_as_external_lsa_get_prefix_str
};
void
ospf6_asbr_init (void)
{
ospf6_routemap_init ();
ospf6_install_lsa_handler (&as_external_handler);
install_element (VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd);
install_element (OSPF6_NODE, &ospf6_redistribute_cmd);
install_element (OSPF6_NODE, &ospf6_redistribute_routemap_cmd);
install_element (OSPF6_NODE, &no_ospf6_redistribute_cmd);
}
void
ospf6_asbr_redistribute_reset (void)
{
int type;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++)
{
if (type == ZEBRA_ROUTE_OSPF6)
continue;
if (ospf6_zebra_is_redistribute (type))
ospf6_asbr_redistribute_unset(type);
}
}
void
ospf6_asbr_terminate (void)
{
route_map_finish ();
}
DEFUN (debug_ospf6_asbr,
debug_ospf6_asbr_cmd,
"debug ospf6 asbr",
DEBUG_STR
OSPF6_STR
"Debug OSPFv3 ASBR function\n"
)
{
OSPF6_DEBUG_ASBR_ON ();
return CMD_SUCCESS;
}
DEFUN (no_debug_ospf6_asbr,
no_debug_ospf6_asbr_cmd,
"no debug ospf6 asbr",
NO_STR
DEBUG_STR
OSPF6_STR
"Debug OSPFv3 ASBR function\n"
)
{
OSPF6_DEBUG_ASBR_OFF ();
return CMD_SUCCESS;
}
int
config_write_ospf6_debug_asbr (struct vty *vty)
{
if (IS_OSPF6_DEBUG_ASBR)
vty_out (vty, "debug ospf6 asbr%s", VNL);
return 0;
}
void
install_element_ospf6_debug_asbr ()
{
install_element (ENABLE_NODE, &debug_ospf6_asbr_cmd);
install_element (ENABLE_NODE, &no_debug_ospf6_asbr_cmd);
install_element (CONFIG_NODE, &debug_ospf6_asbr_cmd);
install_element (CONFIG_NODE, &no_debug_ospf6_asbr_cmd);
}