123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /*
- PIM for Quagga
- Copyright (C) 2008 Everton da Silva Marques
- This program 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 of the License, or
- (at your option) any later version.
- This program 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 this program; see the file COPYING; if not, write to the
- Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
- MA 02110-1301 USA
-
- $QuaggaId: $Format:%an, %ai, %h$ $
- */
- #include <zebra.h>
- #include "log.h"
- #include "pimd.h"
- #include "pim_pim.h"
- #include "pim_str.h"
- #include "pim_tlv.h"
- #include "pim_util.h"
- #include "pim_hello.h"
- #include "pim_iface.h"
- #include "pim_neighbor.h"
- #include "pim_upstream.h"
- static void on_trace(const char *label,
- struct interface *ifp, struct in_addr src)
- {
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[100];
- pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
- zlog_debug("%s: from %s on %s",
- label, src_str, ifp->name);
- }
- }
- static void tlv_trace_bool(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset, int value)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d",
- label,
- src_str, ifname,
- tlv_name, value);
- }
- }
- static void tlv_trace_uint16(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset, uint16_t value)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
- label,
- src_str, ifname,
- tlv_name, value);
- }
- }
- static void tlv_trace_uint32(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset, uint32_t value)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u",
- label,
- src_str, ifname,
- tlv_name, value);
- }
- }
- static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset, uint32_t value)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x",
- label,
- src_str, ifname,
- tlv_name, value);
- }
- }
- #if 0
- static void tlv_trace(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s",
- label,
- src_str, ifname,
- tlv_name);
- }
- }
- #endif
- static void tlv_trace_list(const char *label, const char *tlv_name,
- const char *ifname, struct in_addr src_addr,
- int isset, struct list *addr_list)
- {
- if (isset) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
- label,
- src_str, ifname,
- tlv_name,
- addr_list ? ((int) listcount(addr_list)) : -1,
- (void *) addr_list);
- }
- }
- #define FREE_ADDR_LIST \
- if (hello_option_addr_list) { \
- list_delete(hello_option_addr_list); \
- }
- #define FREE_ADDR_LIST_THEN_RETURN(code) \
- { \
- FREE_ADDR_LIST \
- return (code); \
- }
- int pim_hello_recv(struct interface *ifp,
- struct in_addr src_addr,
- uint8_t *tlv_buf, int tlv_buf_size)
- {
- struct pim_interface *pim_ifp;
- struct pim_neighbor *neigh;
- uint8_t *tlv_curr;
- uint8_t *tlv_pastend;
- pim_hello_options hello_options = 0; /* bit array recording options found */
- uint16_t hello_option_holdtime = 0;
- uint16_t hello_option_propagation_delay = 0;
- uint16_t hello_option_override_interval = 0;
- uint32_t hello_option_dr_priority = 0;
- uint32_t hello_option_generation_id = 0;
- struct list *hello_option_addr_list = 0;
- if (PIM_DEBUG_PIM_HELLO)
- on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
- pim_ifp = ifp->info;
- zassert(pim_ifp);
- ++pim_ifp->pim_ifstat_hello_recv;
- /*
- Parse PIM hello TLVs
- */
- zassert(tlv_buf_size >= 0);
- tlv_curr = tlv_buf;
- tlv_pastend = tlv_buf + tlv_buf_size;
- while (tlv_curr < tlv_pastend) {
- uint16_t option_type;
- uint16_t option_len;
- int remain = tlv_pastend - tlv_curr;
- if (remain < PIM_TLV_MIN_SIZE) {
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
- __PRETTY_FUNCTION__,
- remain, PIM_TLV_MIN_SIZE,
- src_str, ifp->name);
- }
- FREE_ADDR_LIST_THEN_RETURN(-1);
- }
- option_type = PIM_TLV_GET_TYPE(tlv_curr);
- tlv_curr += PIM_TLV_TYPE_SIZE;
- option_len = PIM_TLV_GET_LENGTH(tlv_curr);
- tlv_curr += PIM_TLV_LENGTH_SIZE;
- if ((tlv_curr + option_len) > tlv_pastend) {
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
- __PRETTY_FUNCTION__,
- option_type, option_len, tlv_pastend - tlv_curr,
- src_str, ifp->name);
- }
- FREE_ADDR_LIST_THEN_RETURN(-2);
- }
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
- __PRETTY_FUNCTION__,
- remain,
- option_type, option_len,
- src_str, ifp->name);
- }
- switch (option_type) {
- case PIM_MSG_OPTION_TYPE_HOLDTIME:
- if (pim_tlv_parse_holdtime(ifp->name, src_addr,
- &hello_options,
- &hello_option_holdtime,
- option_len,
- tlv_curr)) {
- FREE_ADDR_LIST_THEN_RETURN(-3);
- }
- break;
- case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
- if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr,
- &hello_options,
- &hello_option_propagation_delay,
- &hello_option_override_interval,
- option_len,
- tlv_curr)) {
- FREE_ADDR_LIST_THEN_RETURN(-4);
- }
- break;
- case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
- if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
- &hello_options,
- &hello_option_dr_priority,
- option_len,
- tlv_curr)) {
- FREE_ADDR_LIST_THEN_RETURN(-5);
- }
- break;
- case PIM_MSG_OPTION_TYPE_GENERATION_ID:
- if (pim_tlv_parse_generation_id(ifp->name, src_addr,
- &hello_options,
- &hello_option_generation_id,
- option_len,
- tlv_curr)) {
- FREE_ADDR_LIST_THEN_RETURN(-6);
- }
- break;
- case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
- if (pim_tlv_parse_addr_list(ifp->name, src_addr,
- &hello_options,
- &hello_option_addr_list,
- option_len,
- tlv_curr)) {
- return -7;
- }
- break;
- case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
- __PRETTY_FUNCTION__,
- option_type, option_len,
- src_str, ifp->name);
- }
- break;
- default:
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
- __PRETTY_FUNCTION__,
- option_type, option_len,
- src_str, ifp->name);
- }
- }
- tlv_curr += option_len;
- }
- /*
- Check received PIM hello options
- */
- if (PIM_DEBUG_PIM_HELLO) {
- tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME),
- hello_option_holdtime);
- tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
- hello_option_propagation_delay);
- tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
- hello_option_override_interval);
- tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY),
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
- tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY),
- hello_option_dr_priority);
- tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID),
- hello_option_generation_id);
- tlv_trace_list(__PRETTY_FUNCTION__, "address_list",
- ifp->name, src_addr,
- PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST),
- hello_option_addr_list);
- }
- if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: PIM hello missing holdtime from %s on interface %s",
- __PRETTY_FUNCTION__,
- src_str, ifp->name);
- }
- }
- /*
- New neighbor?
- */
- neigh = pim_neighbor_find(ifp, src_addr);
- if (!neigh) {
- /* Add as new neighbor */
-
- neigh = pim_neighbor_add(ifp, src_addr,
- hello_options,
- hello_option_holdtime,
- hello_option_propagation_delay,
- hello_option_override_interval,
- hello_option_dr_priority,
- hello_option_generation_id,
- hello_option_addr_list);
- if (!neigh) {
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_warn("%s: failure creating PIM neighbor %s on interface %s",
- __PRETTY_FUNCTION__,
- src_str, ifp->name);
- }
- FREE_ADDR_LIST_THEN_RETURN(-8);
- }
- /* actual addr list has been saved under neighbor */
- return 0;
- }
- /*
- Received generation ID ?
- */
-
- if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
- /* GenID mismatch ? */
- if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ||
- (hello_option_generation_id != neigh->generation_id)) {
- /* GenID changed */
- pim_upstream_rpf_genid_changed(neigh->source_addr);
- /* GenID mismatch, then replace neighbor */
-
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
- __PRETTY_FUNCTION__,
- hello_option_generation_id,
- neigh->generation_id,
- src_str, ifp->name);
- }
- pim_upstream_rpf_genid_changed(neigh->source_addr);
-
- pim_neighbor_delete(ifp, neigh, "GenID mismatch");
- neigh = pim_neighbor_add(ifp, src_addr,
- hello_options,
- hello_option_holdtime,
- hello_option_propagation_delay,
- hello_option_override_interval,
- hello_option_dr_priority,
- hello_option_generation_id,
- hello_option_addr_list);
- if (!neigh) {
- if (PIM_DEBUG_PIM_HELLO) {
- char src_str[100];
- pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
- zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s",
- __PRETTY_FUNCTION__,
- src_str, ifp->name);
- }
- FREE_ADDR_LIST_THEN_RETURN(-9);
- }
- /* actual addr list is saved under neighbor */
- return 0;
- } /* GenId mismatch: replace neighbor */
-
- } /* GenId received */
- /*
- Update existing neighbor
- */
- pim_neighbor_update(neigh,
- hello_options,
- hello_option_holdtime,
- hello_option_dr_priority,
- hello_option_addr_list);
- /* actual addr list is saved under neighbor */
- return 0;
- }
- int pim_hello_build_tlv(const char *ifname,
- uint8_t *tlv_buf, int tlv_buf_size,
- uint16_t holdtime,
- uint32_t dr_priority,
- uint32_t generation_id,
- uint16_t propagation_delay,
- uint16_t override_interval,
- int can_disable_join_suppression,
- struct list *ifconnected)
- {
- uint8_t *curr = tlv_buf;
- uint8_t *pastend = tlv_buf + tlv_buf_size;
- uint8_t *tmp;
- /*
- * Append options
- */
- /* Holdtime */
- curr = pim_tlv_append_uint16(curr,
- pastend,
- PIM_MSG_OPTION_TYPE_HOLDTIME,
- holdtime);
- if (!curr) {
- if (PIM_DEBUG_PIM_HELLO) {
- zlog_debug("%s: could not set PIM hello Holdtime option for interface %s",
- __PRETTY_FUNCTION__, ifname);
- }
- return -1;
- }
- /* LAN Prune Delay */
- tmp = pim_tlv_append_2uint16(curr,
- pastend,
- PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
- propagation_delay,
- override_interval);
- if (!tmp) {
- if (PIM_DEBUG_PIM_HELLO) {
- zlog_debug("%s: could not set PIM LAN Prune Delay option for interface %s",
- __PRETTY_FUNCTION__, ifname);
- }
- return -1;
- }
- if (can_disable_join_suppression) {
- *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */
- }
- curr = tmp;
- /* DR Priority */
- curr = pim_tlv_append_uint32(curr,
- pastend,
- PIM_MSG_OPTION_TYPE_DR_PRIORITY,
- dr_priority);
- if (!curr) {
- if (PIM_DEBUG_PIM_HELLO) {
- zlog_debug("%s: could not set PIM hello DR Priority option for interface %s",
- __PRETTY_FUNCTION__, ifname);
- }
- return -2;
- }
- /* Generation ID */
- curr = pim_tlv_append_uint32(curr,
- pastend,
- PIM_MSG_OPTION_TYPE_GENERATION_ID,
- generation_id);
- if (!curr) {
- if (PIM_DEBUG_PIM_HELLO) {
- zlog_debug("%s: could not set PIM hello Generation ID option for interface %s",
- __PRETTY_FUNCTION__, ifname);
- }
- return -3;
- }
- /* Secondary Address List */
- if (ifconnected) {
- curr = pim_tlv_append_addrlist_ucast(curr,
- pastend,
- ifconnected);
- if (!curr) {
- if (PIM_DEBUG_PIM_HELLO) {
- zlog_debug("%s: could not set PIM hello Secondary Address List option for interface %s",
- __PRETTY_FUNCTION__, ifname);
- }
- return -4;
- }
- }
- return curr - tlv_buf;
- }
- /*
- RFC 4601: 4.3.1. Sending Hello Messages
- Thus, if a router needs to send a Join/Prune or Assert message on an
- interface on which it has not yet sent a Hello message with the
- currently configured IP address, then it MUST immediately send the
- relevant Hello message without waiting for the Hello Timer to
- expire, followed by the Join/Prune or Assert message.
- */
- void pim_hello_require(struct interface *ifp)
- {
- struct pim_interface *pim_ifp;
- zassert(ifp);
- pim_ifp = ifp->info;
- zassert(pim_ifp);
- if (pim_ifp->pim_ifstat_hello_sent)
- return;
- pim_hello_restart_now(ifp); /* Send hello and restart timer */
- }
|