diff options
Diffstat (limited to 'zebra/irdp_interface.c')
-rw-r--r-- | zebra/irdp_interface.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c new file mode 100644 index 0000000..253e6a8 --- /dev/null +++ b/zebra/irdp_interface.c @@ -0,0 +1,716 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (C) 1997, 2000 + * Portions: + * Swedish University of Agricultural Sciences + * Robert Olsson + * Kunihiro Ishiguro + * + * Thanks to Jens Laas at Swedish University of Agricultural Sciences + * for reviewing and tests. + */ + +#include <zebra.h> + +#include "if.h" +#include "vty.h" +#include "sockunion.h" +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "stream.h" +#include "ioctl.h" +#include "connected.h" +#include "log.h" +#include "zclient.h" +#include "frrevent.h" +#include "lib_errors.h" +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zebra_router.h" +#include "zebra/redistribute.h" +#include "zebra/irdp.h" +#include "zebra/zebra_errors.h" +#include <netinet/ip_icmp.h> +#include "if.h" +#include "sockunion.h" +#include "log.h" +#include "network.h" + +extern int irdp_sock; + +DEFINE_MTYPE_STATIC(ZEBRA, IRDP_IF, "IRDP interface data"); + +#define IRDP_CONFIGED \ + do { \ + if (!irdp) { \ + vty_out(vty, \ + "Please Configure IRDP before using this command\n"); \ + return CMD_WARNING_CONFIG_FAILED; \ + } \ + } while (0) + +static struct irdp_interface *irdp_if_get(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + + if (!zi) + return NULL; + + if (!zi->irdp) + zi->irdp = XCALLOC(MTYPE_IRDP_IF, sizeof(*zi->irdp)); + + if (!zi->irdp->started) + return NULL; + + return zi->irdp; +} + +static int irdp_if_delete(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + if (!zi) + return 0; + XFREE(MTYPE_IRDP_IF, zi->irdp); + return 0; +} + +static const char *inet_2a(uint32_t a, char *b, size_t b_len) +{ + snprintf(b, b_len, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, + (a >> 16) & 0xFF, (a >> 24) & 0xFF); + return b; +} + + +static struct prefix *irdp_get_prefix(struct interface *ifp) +{ + struct listnode *node; + struct connected *ifc; + + if (ifp->connected) + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) + return ifc->address; + + return NULL; +} + +/* Join to the add/leave multicast group. */ +static int if_group(struct interface *ifp, int sock, uint32_t group, + int add_leave) +{ + struct ip_mreq m; + struct prefix *p; + int ret; + char b1[INET_ADDRSTRLEN]; + + memset(&m, 0, sizeof(m)); + m.imr_multiaddr.s_addr = htonl(group); + p = irdp_get_prefix(ifp); + + if (!p) { + flog_warn(EC_ZEBRA_NO_IFACE_ADDR, + "IRDP: can't get address for %s", ifp->name); + return 1; + } + + m.imr_interface = p->u.prefix4; + + ret = setsockopt(sock, IPPROTO_IP, add_leave, (char *)&m, + sizeof(struct ip_mreq)); + if (ret < 0) + flog_err_sys(EC_LIB_SOCKET, "IRDP: %s can't setsockopt %s: %s", + add_leave == IP_ADD_MEMBERSHIP ? "join group" + : "leave group", + inet_2a(group, b1, sizeof(b1)), + safe_strerror(errno)); + + return ret; +} + +static int if_add_group(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + int ret; + char b1[INET_ADDRSTRLEN]; + + if (!irdp) + return -1; + + ret = if_group(ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_ADD_MEMBERSHIP); + if (ret < 0) { + return ret; + } + + if (irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: Adding group %s for %s", + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); + return 0; +} + +static int if_drop_group(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + int ret; + char b1[INET_ADDRSTRLEN]; + + if (!irdp) + return -1; + + ret = if_group(ifp, irdp_sock, INADDR_ALLRTRS_GROUP, + IP_DROP_MEMBERSHIP); + if (ret < 0) + return ret; + + if (irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: Leaving group %s for %s", + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); + return 0; +} + +static void if_set_defaults(struct irdp_interface *irdp) +{ + irdp->MaxAdvertInterval = IRDP_MAXADVERTINTERVAL; + irdp->MinAdvertInterval = IRDP_MINADVERTINTERVAL; + irdp->Preference = IRDP_PREFERENCE; + irdp->Lifetime = IRDP_LIFETIME; +} + + +static struct Adv *Adv_new(void) +{ + return XCALLOC(MTYPE_IRDP_IF, sizeof(struct Adv)); +} + +static void Adv_free(struct Adv *adv) +{ + XFREE(MTYPE_IRDP_IF, adv); +} + +static void irdp_if_start(struct interface *ifp, int multicast, + int set_defaults) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + struct listnode *node; + struct connected *ifc; + uint32_t timer, seed; + + assert(irdp); + + irdp->started = true; + if (irdp->flags & IF_ACTIVE) { + zlog_debug("IRDP: Interface is already active %s", ifp->name); + return; + } + if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) { + flog_warn(EC_ZEBRA_IRDP_CANNOT_ACTIVATE_IFACE, + "IRDP: Cannot activate interface %s (cannot create IRDP socket)", + ifp->name); + return; + } + irdp->flags |= IF_ACTIVE; + + if (!multicast) + irdp->flags |= IF_BROADCAST; + + if_add_update(ifp); + + if (!(ifp->flags & IFF_UP)) { + flog_warn(EC_ZEBRA_IRDP_IFACE_DOWN, + "IRDP: Interface is down %s", ifp->name); + } + + /* Shall we cancel if_start if if_add_group fails? */ + + if (multicast) { + if_add_group(ifp); + + if (!(ifp->flags & (IFF_MULTICAST | IFF_ALLMULTI))) { + flog_warn(EC_ZEBRA_IRDP_IFACE_MCAST_DISABLED, + "IRDP: Interface not multicast enabled %s", + ifp->name); + } + } + + if (set_defaults) + if_set_defaults(irdp); + + irdp->irdp_sent = 0; + + /* The spec suggests this for randomness */ + + seed = 0; + if (ifp->connected) + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + seed = ifc->address->u.prefix4.s_addr; + break; + } + + srandom(seed); + timer = (frr_weak_random() % IRDP_DEFAULT_INTERVAL) + 1; + + irdp->AdvPrefList = list_new(); + irdp->AdvPrefList->del = (void (*)(void *))Adv_free; /* Destructor */ + + + /* And this for startup. Speed limit from 1991 :-). But it's OK*/ + + if (irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS + && timer > MAX_INITIAL_ADVERT_INTERVAL) + timer = MAX_INITIAL_ADVERT_INTERVAL; + + + if (irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: Init timer for %s set to %u", ifp->name, + timer); + + irdp->t_advertise = NULL; + event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, + &irdp->t_advertise); +} + +static void irdp_if_stop(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + + if (irdp == NULL) { + zlog_debug("Interface %s structure is NULL", ifp->name); + return; + } + + if (!(irdp->flags & IF_ACTIVE)) { + zlog_debug("Interface is not active %s", ifp->name); + return; + } + + if (!(irdp->flags & IF_BROADCAST)) + if_drop_group(ifp); + + irdp_advert_off(ifp); + + list_delete(&irdp->AdvPrefList); + + irdp->flags = 0; +} + + +static void irdp_if_shutdown(struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + + if (!irdp) + return; + + if (irdp->flags & IF_SHUTDOWN) { + zlog_debug("IRDP: Interface is already shutdown %s", ifp->name); + return; + } + + irdp->flags |= IF_SHUTDOWN; + irdp->flags &= ~IF_ACTIVE; + + if (!(irdp->flags & IF_BROADCAST)) + if_drop_group(ifp); + + /* Tell the hosts we are out of service */ + irdp_advert_off(ifp); +} + +static void irdp_if_no_shutdown(struct interface *ifp) +{ + struct irdp_interface *irdp = irdp_if_get(ifp); + + if (!irdp) + return; + + if (!(irdp->flags & IF_SHUTDOWN)) { + zlog_debug("IRDP: Interface is not shutdown %s", ifp->name); + return; + } + + irdp->flags &= ~IF_SHUTDOWN; + + irdp_if_start(ifp, irdp->flags & IF_BROADCAST ? false : true, false); +} + + +/* Write configuration to user */ + +int irdp_config_write(struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zi = ifp->info; + struct irdp_interface *irdp = zi->irdp; + struct Adv *adv; + struct listnode *node; + char b1[INET_ADDRSTRLEN]; + + if (!irdp) + return 0; + + if (irdp->flags & IF_ACTIVE || irdp->flags & IF_SHUTDOWN) { + + if (irdp->flags & IF_SHUTDOWN) + vty_out(vty, " ip irdp shutdown \n"); + + if (irdp->flags & IF_BROADCAST) + vty_out(vty, " ip irdp broadcast\n"); + else + vty_out(vty, " ip irdp multicast\n"); + + vty_out(vty, " ip irdp preference %ld\n", irdp->Preference); + + for (ALL_LIST_ELEMENTS_RO(irdp->AdvPrefList, node, adv)) + vty_out(vty, " ip irdp address %s preference %d\n", + inet_2a(adv->ip.s_addr, b1, sizeof(b1)), + adv->pref); + + vty_out(vty, " ip irdp holdtime %d\n", irdp->Lifetime); + + vty_out(vty, " ip irdp minadvertinterval %ld\n", + irdp->MinAdvertInterval); + + vty_out(vty, " ip irdp maxadvertinterval %ld\n", + irdp->MaxAdvertInterval); + } + return 0; +} + + +DEFUN (ip_irdp_multicast, + ip_irdp_multicast_cmd, + "ip irdp multicast", + IP_STR + "ICMP Router discovery on this interface\n" + "Use multicast mode\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + irdp_if_get(ifp); + + irdp_if_start(ifp, true, true); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_broadcast, + ip_irdp_broadcast_cmd, + "ip irdp broadcast", + IP_STR + "ICMP Router discovery on this interface\n" + "Use broadcast mode\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + irdp_if_get(ifp); + + irdp_if_start(ifp, false, true); + return CMD_SUCCESS; +} + +DEFUN (no_ip_irdp, + no_ip_irdp_cmd, + "no ip irdp", + NO_STR + IP_STR + "Disable ICMP Router discovery on this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + irdp_if_stop(ifp); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_shutdown, + ip_irdp_shutdown_cmd, + "ip irdp shutdown", + IP_STR + "ICMP Router discovery on this interface\n" + "ICMP Router discovery shutdown on this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + irdp_if_shutdown(ifp); + return CMD_SUCCESS; +} + +DEFUN (no_ip_irdp_shutdown, + no_ip_irdp_shutdown_cmd, + "no ip irdp shutdown", + NO_STR + IP_STR + "ICMP Router discovery on this interface\n" + "ICMP Router discovery no shutdown on this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + irdp_if_no_shutdown(ifp); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_holdtime, + ip_irdp_holdtime_cmd, + "ip irdp holdtime (0-9000)", + IP_STR + "ICMP Router discovery on this interface\n" + "Set holdtime value\n" + "Holdtime value in seconds. Default is 1800 seconds\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->Lifetime = atoi(argv[idx_number]->arg); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_minadvertinterval, + ip_irdp_minadvertinterval_cmd, + "ip irdp minadvertinterval (3-1800)", + IP_STR + "ICMP Router discovery on this interface\n" + "Set minimum time between advertisement\n" + "Minimum advertisement interval in seconds\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + if ((unsigned)atoi(argv[idx_number]->arg) <= irdp->MaxAdvertInterval) { + irdp->MinAdvertInterval = atoi(argv[idx_number]->arg); + return CMD_SUCCESS; + } else { + vty_out(vty, + "%% MinAdvertInterval must be less than or equal to MaxAdvertInterval\n"); + return CMD_WARNING_CONFIG_FAILED; + } +} + +DEFUN (ip_irdp_maxadvertinterval, + ip_irdp_maxadvertinterval_cmd, + "ip irdp maxadvertinterval (4-1800)", + IP_STR + "ICMP Router discovery on this interface\n" + "Set maximum time between advertisement\n" + "Maximum advertisement interval in seconds\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + if (irdp->MinAdvertInterval <= (unsigned)atoi(argv[idx_number]->arg)) { + irdp->MaxAdvertInterval = atoi(argv[idx_number]->arg); + return CMD_SUCCESS; + } else { + vty_out(vty, + "%% MaxAdvertInterval must be greater than or equal to MinAdvertInterval\n"); + return CMD_WARNING_CONFIG_FAILED; + } +} + +/* DEFUN needs to be fixed for negative ranages... + * "ip irdp preference <-2147483648-2147483647>", + * Be positive for now. :-) + */ + +DEFUN (ip_irdp_preference, + ip_irdp_preference_cmd, + "ip irdp preference (0-2147483647)", + IP_STR + "ICMP Router discovery on this interface\n" + "Set default preference level for this interface\n" + "Preference level\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->Preference = atoi(argv[idx_number]->arg); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_address_preference, + ip_irdp_address_preference_cmd, + "ip irdp address A.B.C.D preference (0-2147483647)", + IP_STR + "Alter ICMP Router discovery preference on this interface\n" + "Set IRDP address for advertise\n" + "IPv4 address\n" + "Specify IRDP non-default preference to advertise\n" + "Preference level\n") +{ + int idx_ipv4 = 3; + int idx_number = 5; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + struct listnode *node; + struct in_addr ip; + int pref; + int ret; + struct Adv *adv; + + IRDP_CONFIGED; + + ret = inet_aton(argv[idx_ipv4]->arg, &ip); + if (!ret) + return CMD_WARNING_CONFIG_FAILED; + + pref = atoi(argv[idx_number]->arg); + + for (ALL_LIST_ELEMENTS_RO(irdp->AdvPrefList, node, adv)) + if (adv->ip.s_addr == ip.s_addr) + return CMD_SUCCESS; + + adv = Adv_new(); + adv->ip = ip; + adv->pref = pref; + listnode_add(irdp->AdvPrefList, adv); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_irdp_address_preference, + no_ip_irdp_address_preference_cmd, + "no ip irdp address A.B.C.D preference (0-2147483647)", + NO_STR + IP_STR + "Alter ICMP Router discovery preference on this interface\n" + "Select IRDP address\n" + "IPv4 address\n" + "Reset ICMP Router discovery preference on this interface\n" + "Old preference level\n") +{ + int idx_ipv4 = 4; + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + struct listnode *node, *nnode; + struct in_addr ip; + int ret; + struct Adv *adv; + + IRDP_CONFIGED; + + ret = inet_aton(argv[idx_ipv4]->arg, &ip); + if (!ret) + return CMD_WARNING_CONFIG_FAILED; + + for (ALL_LIST_ELEMENTS(irdp->AdvPrefList, node, nnode, adv)) { + if (adv->ip.s_addr == ip.s_addr) { + listnode_delete(irdp->AdvPrefList, adv); + break; + } + } + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_messages, + ip_irdp_debug_messages_cmd, + "ip irdp debug messages", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n" + "IRDP debugging options\n" + "Enable debugging for IRDP messages\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->flags |= IF_DEBUG_MESSAGES; + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_misc, + ip_irdp_debug_misc_cmd, + "ip irdp debug misc", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n" + "IRDP debugging options\n" + "Enable debugging for miscellaneous IRDP events\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->flags |= IF_DEBUG_MISC; + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_packet, + ip_irdp_debug_packet_cmd, + "ip irdp debug packet", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n" + "IRDP debugging options\n" + "Enable debugging for IRDP packets\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->flags |= IF_DEBUG_PACKET; + + return CMD_SUCCESS; +} + + +DEFUN (ip_irdp_debug_disable, + ip_irdp_debug_disable_cmd, + "ip irdp debug disable", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n" + "IRDP debugging options\n" + "Disable debugging for all IRDP events\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct irdp_interface *irdp = irdp_if_get(ifp); + + IRDP_CONFIGED; + + irdp->flags &= ~IF_DEBUG_PACKET; + irdp->flags &= ~IF_DEBUG_MESSAGES; + irdp->flags &= ~IF_DEBUG_MISC; + + return CMD_SUCCESS; +} + +void irdp_if_init(void) +{ + hook_register(zebra_if_config_wr, irdp_config_write); + hook_register(if_del, irdp_if_delete); + + install_element(INTERFACE_NODE, &ip_irdp_broadcast_cmd); + install_element(INTERFACE_NODE, &ip_irdp_multicast_cmd); + install_element(INTERFACE_NODE, &no_ip_irdp_cmd); + install_element(INTERFACE_NODE, &ip_irdp_shutdown_cmd); + install_element(INTERFACE_NODE, &no_ip_irdp_shutdown_cmd); + install_element(INTERFACE_NODE, &ip_irdp_holdtime_cmd); + install_element(INTERFACE_NODE, &ip_irdp_maxadvertinterval_cmd); + install_element(INTERFACE_NODE, &ip_irdp_minadvertinterval_cmd); + install_element(INTERFACE_NODE, &ip_irdp_preference_cmd); + install_element(INTERFACE_NODE, &ip_irdp_address_preference_cmd); + install_element(INTERFACE_NODE, &no_ip_irdp_address_preference_cmd); + + install_element(INTERFACE_NODE, &ip_irdp_debug_messages_cmd); + install_element(INTERFACE_NODE, &ip_irdp_debug_misc_cmd); + install_element(INTERFACE_NODE, &ip_irdp_debug_packet_cmd); + install_element(INTERFACE_NODE, &ip_irdp_debug_disable_cmd); +} |