From 1cfaf93c5e7512bf235613f651ef21440fbde792 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 10 Nov 2016 12:53:21 -0200 Subject: [PATCH 01/13] ripd: reject authentication strings with zeros in the middle RFC 2453 says: "If the password is under 16 octets, it must be left-justified and padded to the right with nulls (0x00)". Fixes IxANVL RIP test 10.3. Signed-off-by: Renato Westphal --- ripd/ripd.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index 6124471161..ce7a6d1e99 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -812,7 +812,15 @@ rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from, struct interface *ifp) { struct rip_interface *ri; - char *auth_str; + char *auth_str = (char *) &rte->prefix; + int i; + + /* reject passwords with zeros in the middle of the string */ + for (i = strlen (auth_str); i < 16; i++) + { + if (auth_str[i] != '\0') + return 0; + } if (IS_RIP_DEBUG_EVENT) zlog_debug ("RIPv2 simple password authentication from %s", @@ -827,8 +835,6 @@ rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from, /* Simple password authentication. */ if (ri->auth_str) { - auth_str = (char *) &rte->prefix; - if (strncmp (auth_str, ri->auth_str, 16) == 0) return 1; } @@ -841,7 +847,7 @@ rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from, if (keychain == NULL) return 0; - key = key_match_for_accept (keychain, (char *) &rte->prefix); + key = key_match_for_accept (keychain, auth_str); if (key) return 1; } From 4e0548665d50db273cceddb0210d5be9d25688c1 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 10 Nov 2016 12:54:07 -0200 Subject: [PATCH 02/13] ripd: ignore request for RTEs from unknown address-families Fixes IxANVL RIP test 6.5. Signed-off-by: Renato Westphal --- ripd/ripd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ripd/ripd.c b/ripd/ripd.c index ce7a6d1e99..d84f863de2 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1687,6 +1687,9 @@ rip_request_process (struct rip_packet *packet, int size, } else { + if (ntohs (rte->family) != AF_INET) + return; + /* Examine the list of RTEs in the Request one by one. For each entry, look up the destination in the router's routing database and, if there is a route, put that route's metric in From f90310cfe8a5d17357a0e31cb7e55eb7d6024092 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 10 Nov 2016 12:55:09 -0200 Subject: [PATCH 03/13] ripd: implement the "ip rip v2-broadcast" CLI command This command allows ripd to send v2 updates as broadcast packets instead of multicast packets. Useful as a technique to help with RIPv1/v2 interop issues. Fixes IxANVL RIP test 16.2 Signed-off-by: Renato Westphal --- ripd/rip_interface.c | 46 +++++++++++++++++++++++++++++++++++++++++++- ripd/ripd.c | 10 ++++++---- ripd/ripd.h | 3 +++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 359549ed80..0d412ee997 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -536,7 +536,9 @@ rip_interface_reset (struct rip_interface *ri) ri->ri_send = RI_RIP_UNSPEC; ri->ri_receive = RI_RIP_UNSPEC; - + + ri->v2_broadcast = 0; + if (ri->auth_str) { free (ri->auth_str); @@ -1518,6 +1520,41 @@ ALIAS (no_ip_rip_send_version, "Version 1\n" "Version 2\n") +DEFUN (ip_rip_v2_broadcast, + ip_rip_v2_broadcast_cmd, + "ip rip v2-broadcast", + IP_STR + "Routing Information Protocol\n" + "Send ip broadcast v2 update\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + ri->v2_broadcast = 1; + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_v2_broadcast, + no_ip_rip_v2_broadcast_cmd, + "no ip rip v2-broadcast", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Send ip broadcast v2 update\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + ri->v2_broadcast = 0; + return CMD_SUCCESS; +} + DEFUN (ip_rip_authentication_mode, ip_rip_authentication_mode_cmd, "ip rip authentication mode (md5|text)", @@ -1918,6 +1955,7 @@ rip_interface_config_write (struct vty *vty) (ri->ri_send == RI_RIP_UNSPEC) && (ri->ri_receive == RI_RIP_UNSPEC) && (ri->auth_type != RIP_AUTH_MD5) && + (!ri->v2_broadcast) && (ri->md5_auth_len != RIP_AUTH_MD5_SIZE) && (!ri->auth_str) && (!ri->key_chain) ) @@ -1959,6 +1997,9 @@ rip_interface_config_write (struct vty *vty) lookup (ri_version_msg, ri->ri_receive), VTY_NEWLINE); + if (ri->v2_broadcast) + vty_out (vty, " ip rip v2-broadcast%s", VTY_NEWLINE); + /* RIP authentication. */ if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD) vty_out (vty, " ip rip authentication mode text%s", VTY_NEWLINE); @@ -2099,6 +2140,9 @@ rip_if_init (void) install_element (INTERFACE_NODE, &no_ip_rip_receive_version_cmd); install_element (INTERFACE_NODE, &no_ip_rip_receive_version_num_cmd); + install_element (INTERFACE_NODE, &ip_rip_v2_broadcast_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_v2_broadcast_cmd); + install_element (INTERFACE_NODE, &ip_rip_authentication_mode_cmd); install_element (INTERFACE_NODE, &ip_rip_authentication_mode_authlen_cmd); install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_cmd); diff --git a/ripd/ripd.c b/ripd/ripd.c index d84f863de2..97e80ed43b 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -2443,20 +2443,22 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, static void rip_update_interface (struct connected *ifc, u_char version, int route_type) { + struct interface *ifp = ifc->ifp; + struct rip_interface *ri = ifp->info; struct sockaddr_in to; /* When RIP version is 2 and multicast enable interface. */ - if (version == RIPv2 && if_is_multicast (ifc->ifp)) + if (version == RIPv2 && !ri->v2_broadcast && if_is_multicast (ifp)) { if (IS_RIP_DEBUG_EVENT) - zlog_debug ("multicast announce on %s ", ifc->ifp->name); + zlog_debug ("multicast announce on %s ", ifp->name); rip_output_process (ifc, NULL, route_type, version); return; } /* If we can't send multicast packet, send it with unicast. */ - if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp)) + if (if_is_broadcast (ifp) || if_is_pointopoint (ifp)) { if (ifc->address->family == AF_INET) { @@ -2478,7 +2480,7 @@ rip_update_interface (struct connected *ifc, u_char version, int route_type) if (IS_RIP_DEBUG_EVENT) zlog_debug("%s announce to %s on %s", CONNECTED_PEER(ifc) ? "unicast" : "broadcast", - inet_ntoa (to.sin_addr), ifc->ifp->name); + inet_ntoa (to.sin_addr), ifp->name); rip_output_process (ifc, &to, route_type, version); } diff --git a/ripd/ripd.h b/ripd/ripd.h index 3de23ec334..1c212a081b 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -258,6 +258,9 @@ struct rip_interface int ri_send; int ri_receive; + /* RIPv2 broadcast mode */ + int v2_broadcast; + /* RIPv2 authentication type. */ int auth_type; From 6aec4b41760e6ba54fc52b09f6d3ecfe173f6ee6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 10 Nov 2016 13:15:43 -0200 Subject: [PATCH 04/13] ripd: add "none" option to the "ip rip receive version" command RFC 2453 says (section 5.1): "(...) For completeness, routers should also implement a receive control switch which would determine whether to accept, RIP-1 only, RIP-2 only, both, or none. It should also be configurable on a per-interface basis". For the "ip rip send version" command, we don't need to implement the "none" option because there's already the "passive-interface" command for that. Fixes IxANVL RIP test 16.8. Signed-off-by: Renato Westphal --- ripd/rip_interface.c | 20 +++++++++++++------- ripd/ripd.c | 12 +++--------- ripd/ripd.h | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 0d412ee997..6d6cb0c686 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -55,6 +55,7 @@ const struct message ri_version_msg[] = {RI_RIP_VERSION_1, "1"}, {RI_RIP_VERSION_2, "2"}, {RI_RIP_VERSION_1_AND_2, "1 2"}, + {RI_RIP_VERSION_NONE, "none"}, }; extern struct zebra_privs_t ripd_privs; @@ -1318,13 +1319,14 @@ DEFUN (no_rip_neighbor, DEFUN (ip_rip_receive_version, ip_rip_receive_version_cmd, - "ip rip receive version (1|2)", + "ip rip receive version (1|2|none)", IP_STR "Routing Information Protocol\n" "Advertisement reception\n" "Version control\n" "RIP version 1\n" - "RIP version 2\n") + "RIP version 2\n" + "None\n") { struct interface *ifp; struct rip_interface *ri; @@ -1332,17 +1334,21 @@ DEFUN (ip_rip_receive_version, ifp = (struct interface *)vty->index; ri = ifp->info; - /* Version 1. */ - if (atoi (argv[0]) == 1) + switch (*argv[0]) { + case '1': ri->ri_receive = RI_RIP_VERSION_1; return CMD_SUCCESS; - } - if (atoi (argv[0]) == 2) - { + case '2': ri->ri_receive = RI_RIP_VERSION_2; return CMD_SUCCESS; + case 'n': + ri->ri_receive = RI_RIP_VERSION_NONE; + return CMD_SUCCESS; + default: + break; } + return CMD_WARNING; } diff --git a/ripd/ripd.c b/ripd/ripd.c index 97e80ed43b..f27a817aed 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1937,15 +1937,9 @@ rip_read (struct thread *t) /* RIP Version check. RFC2453, 4.6 and 5.1 */ vrecv = ((ri->ri_receive == RI_RIP_UNSPEC) ? rip->version_recv : ri->ri_receive); - if ((packet->version == RIPv1) && !(vrecv & RIPv1)) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug (" packet's v%d doesn't fit to if version spec", - packet->version); - rip_peer_bad_packet (&from); - return -1; - } - if ((packet->version == RIPv2) && !(vrecv & RIPv2)) + if (vrecv == RI_RIP_VERSION_NONE || + ((packet->version == RIPv1) && !(vrecv & RIPv1)) || + ((packet->version == RIPv2) && !(vrecv & RIPv2))) { if (IS_RIP_DEBUG_PACKET) zlog_debug (" packet's v%d doesn't fit to if version spec", diff --git a/ripd/ripd.h b/ripd/ripd.h index 1c212a081b..b26a1d234c 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -350,6 +350,7 @@ struct rip_md5_data #define RI_RIP_VERSION_1 1 #define RI_RIP_VERSION_2 2 #define RI_RIP_VERSION_1_AND_2 3 +#define RI_RIP_VERSION_NONE 4 /* N.B. stuff will break if (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */ From b10ce841035275844cfe494d471865c8a1d4dda6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 10 Nov 2016 15:35:47 -0200 Subject: [PATCH 05/13] ripd: fix the "neighbor" command. We can't use if_lookup_prefix() in rip_update_process() because this function uses prefix_cmp() internally to try matching an interface address to a static neighbor's address. Since prefix_cmp() tries to match exact prefixes, if_lookup_prefix() was always returning NULL. What we really need here is to use prefix_match(), which checks if one prefix includes the other (e.g. one /24 interface including a /32 static neighbor's address). The fix then is to replace the call to if_lookup_prefix() and use if_lookup_address() instead, which uses prefix_match() internally. Fixes IxANVL RIP test 17.1 Signed-off-by: Renato Westphal --- lib/if.c | 8 ++++---- lib/if.h | 4 ++-- ospfd/ospf_packet.c | 18 +++++++++++------- ripd/ripd.c | 19 +++++++------------ 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/if.c b/lib/if.c index 6ae8500291..b52358fa87 100644 --- a/lib/if.c +++ b/lib/if.c @@ -393,7 +393,7 @@ if_lookup_exact_address (void *src, int family) } /* Lookup interface by IPv4 address. */ -struct interface * +struct connected * if_lookup_address_vrf (void *matchaddr, int family, vrf_id_t vrf_id) { struct listnode *node; @@ -402,7 +402,7 @@ if_lookup_address_vrf (void *matchaddr, int family, vrf_id_t vrf_id) struct listnode *cnode; struct interface *ifp; struct connected *c; - struct interface *match; + struct connected *match; if (family == AF_INET) { @@ -428,14 +428,14 @@ if_lookup_address_vrf (void *matchaddr, int family, vrf_id_t vrf_id) (c->address->prefixlen > bestlen)) { bestlen = c->address->prefixlen; - match = ifp; + match = c; } } } return match; } -struct interface * +struct connected * if_lookup_address (void *matchaddr, int family) { return if_lookup_address_vrf (matchaddr, family, VRF_DEFAULT); diff --git a/lib/if.h b/lib/if.h index 57062cd3fc..9b47159103 100644 --- a/lib/if.h +++ b/lib/if.h @@ -387,7 +387,7 @@ extern int if_cmp_name_func (char *, char *); extern struct interface *if_create (const char *name, int namelen); extern struct interface *if_lookup_by_index (ifindex_t); extern struct interface *if_lookup_exact_address (void *matchaddr, int family); -extern struct interface *if_lookup_address (void *matchaddr, int family); +extern struct connected *if_lookup_address (void *matchaddr, int family); extern struct interface *if_lookup_prefix (struct prefix *prefix); extern void if_update_vrf (struct interface *, const char *name, int namelen, @@ -397,7 +397,7 @@ extern struct interface *if_create_vrf (const char *name, int namelen, extern struct interface *if_lookup_by_index_vrf (ifindex_t, vrf_id_t vrf_id); extern struct interface *if_lookup_exact_address_vrf (void *matchaddr, int family, vrf_id_t vrf_id); -extern struct interface *if_lookup_address_vrf (void *matchaddr, int family, +extern struct connected *if_lookup_address_vrf (void *matchaddr, int family, vrf_id_t vrf_id); extern struct interface *if_lookup_prefix_vrf (struct prefix *prefix, vrf_id_t vrf_id); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index b7ef24409e..72ce5586f6 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2772,6 +2772,7 @@ ospf_read (struct thread *thread) struct ospf_header *ospfh; u_int16_t length; struct interface *ifp; + struct connected *c; /* first of all get interface pointer. */ ospf = THREAD_ARG (thread); @@ -2790,13 +2791,16 @@ ospf_read (struct thread *thread) /* Note that sockopt_iphdrincl_swab_systoh was called in ospf_recv_packet. */ if (ifp == NULL) - /* Handle cases where the platform does not support retrieving the ifindex, - and also platforms (such as Solaris 8) that claim to support ifindex - retrieval but do not. */ - ifp = if_lookup_address ((void *)&iph->ip_src, AF_INET); - - if (ifp == NULL) - return 0; + { + /* Handle cases where the platform does not support retrieving the ifindex, + and also platforms (such as Solaris 8) that claim to support ifindex + retrieval but do not. */ + c = if_lookup_address ((void *)&iph->ip_src, AF_INET); + if (c) + ifp = c->ifp; + if (ifp == NULL) + return 0; + } /* IP Header dump. */ if (IS_DEBUG_OSPF_PACKET(0, RECV)) diff --git a/ripd/ripd.c b/ripd/ripd.c index f27a817aed..79de56b781 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1812,7 +1812,7 @@ rip_read (struct thread *t) int len; int vrecv; socklen_t fromlen; - struct interface *ifp; + struct interface *ifp = NULL; struct connected *ifc; struct rip_interface *ri; struct prefix p; @@ -1845,8 +1845,10 @@ rip_read (struct thread *t) } /* Which interface is this packet comes from. */ - ifp = if_lookup_address ((void *)&from.sin_addr, AF_INET); - + ifc = if_lookup_address ((void *)&from.sin_addr, AF_INET); + if (ifc) + ifp = ifc->ifp; + /* RIP packet received */ if (IS_RIP_DEBUG_EVENT) zlog_debug ("RECV packet from %s port %d on %s", @@ -2544,21 +2546,14 @@ rip_update_process (int route_type) { p = &rp->p; - ifp = if_lookup_prefix (p); - if (! ifp) + connected = if_lookup_address (&p->u.prefix4, AF_INET); + if (! connected) { zlog_warn ("Neighbor %s doesnt have connected interface!", inet_ntoa (p->u.prefix4)); continue; } - if ( (connected = connected_lookup_prefix (ifp, p)) == NULL) - { - zlog_warn ("Neighbor %s doesnt have connected network", - inet_ntoa (p->u.prefix4)); - continue; - } - /* Set destination address and port */ memset (&to, 0, sizeof (struct sockaddr_in)); to.sin_addr = p->u.prefix4; From ae735d2d0e13a6babd796ca171519b9db4feaf81 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 11 Nov 2016 20:19:13 -0200 Subject: [PATCH 06/13] ripd: fix race condition on input processing In the early days of ripd, we supported running RIP on secondary IP addresses. To do that, everytime we needed to send a multicast packet, we would create a new temporary socket for each of the interface's addresses and call bind() to change the source IP of the outgoing packets. The problem with these temporary sockets is that they are more specific than the global RIP socket (bound to INADDR_ANY). Then, even though these sockets only exist for a short amount of time, they can receive some RIP packets that were supposed to be received on the global RIP socket. And since we never read from the temporary sockets, these packets are dropped. Since we don't support secondary addresses anymore, the simplest way to fix this problem is to stop using temporary sockets for sending multicast packets. We are already setting IP_MULTICAST_IF before sending each multicast packet, and in this case the primary address of the selected interface is used as the source IP of the outgoing packets, which is exactly what we want. If we decide to reintroduce support for secondary addresses in the future, we should try one of the following: * Use IP_SENDSRCADDR/IP_PKTINFO to set the source address of the outgoing multicast packets; * Create one permanent UDP socket for each possible interface address, and enable reading on all sockets. Fixes the following IxANVL RIP tests: 7.10 and 14.1. Signed-off-by: Renato Westphal --- ripd/ripd.c | 50 +++++++++----------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index 79de56b781..a83c50c7ce 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1339,25 +1339,18 @@ rip_response_process (struct rip_packet *packet, int size, /* Make socket for RIP protocol. */ static int -rip_create_socket (struct sockaddr_in *from) +rip_create_socket (void) { int ret; int sock; struct sockaddr_in addr; memset (&addr, 0, sizeof (struct sockaddr_in)); - - if (!from) - { - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - addr.sin_len = sizeof (struct sockaddr_in); + addr.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - } else { - memcpy(&addr, from, sizeof(addr)); - } - /* sending port must always be the RIP port */ addr.sin_port = htons (RIP_PORT_DEFAULT); @@ -1412,7 +1405,7 @@ static int rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, struct connected *ifc) { - int ret, send_sock; + int ret; struct sockaddr_in sin; assert (ifc != NULL); @@ -1468,38 +1461,16 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, { sin.sin_port = to->sin_port; sin.sin_addr = to->sin_addr; - send_sock = rip->sock; } else { - struct sockaddr_in from; - sin.sin_port = htons (RIP_PORT_DEFAULT); sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP); - - /* multicast send should bind to local interface address */ - memset (&from, 0, sizeof (from)); - from.sin_family = AF_INET; - from.sin_port = htons (RIP_PORT_DEFAULT); - from.sin_addr = ifc->address->u.prefix4; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - from.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - - /* - * we have to open a new socket for each packet because this - * is the most portable way to bind to a different source - * ipv4 address for each packet. - */ - if ( (send_sock = rip_create_socket (&from)) < 0) - { - zlog_warn("rip_send_packet could not create socket."); - return -1; - } - rip_interface_multicast_set (send_sock, ifc); + + rip_interface_multicast_set (rip->sock, ifc); } - ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin, + ret = sendto (rip->sock, buf, size, 0, (struct sockaddr *)&sin, sizeof (struct sockaddr_in)); if (IS_RIP_DEBUG_EVENT) @@ -1509,9 +1480,6 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, if (ret < 0) zlog_warn ("can't send packet : %s", safe_strerror (errno)); - if (!to) - close(send_sock); - return ret; } @@ -2729,7 +2697,7 @@ rip_create (void) rip->obuf = stream_new (1500); /* Make socket. */ - rip->sock = rip_create_socket (NULL); + rip->sock = rip_create_socket (); if (rip->sock < 0) return rip->sock; From 5736139d4a9192c9ba296d2bddc5bbbd025dfd7f Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 12 Nov 2016 18:39:51 -0200 Subject: [PATCH 07/13] pimd/zebra: fix setting of IP_MULTICAST_LOOP on OpenBSD Linux, FreeBSD and NetBSD (and possibly others too) accept both uint8_t and int for the IP_MULTICAST_LOOP sockoption. OpenBSD, in the other hand, accepts only uint8_t. To make setting IP_MULTICAST_LOOP work on every supported platform, always pass a uint8_t variable as a parameter. Signed-off-by: Renato Westphal --- pimd/pim_sock.c | 2 +- pimd/pim_sock.h | 2 +- pimd/pim_ssmpingd.c | 2 +- zebra/irdp_packet.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index a024e3ae01..90b11dcfd6 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -68,7 +68,7 @@ int pim_socket_raw(int protocol) return fd; } -int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, int loop) +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop) { int fd; diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index f0a1600818..cd29543fa0 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -39,7 +39,7 @@ #define PIM_SOCK_ERR_BIND (-11) /* Can't bind to interface */ int pim_socket_raw(int protocol); -int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, int loop); +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop); int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, ifindex_t ifindex); int pim_socket_join_source(int fd, ifindex_t ifindex, diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index fe88eba271..daa3c65e36 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -151,7 +151,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) } { - int loop = 0; + u_char loop = 0; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *) &loop, sizeof(loop))) { zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 25c7aff26b..290a6180e7 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -282,7 +282,7 @@ send_packet(struct interface *ifp, char buf[256]; struct in_pktinfo *pktinfo; u_long src; - int on; + u_char on; if (!(ifp->flags & IFF_UP)) return; From c5bdb09fd9d18ac0524a14c90f79cfad95afefd7 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 12 Nov 2016 19:05:08 -0200 Subject: [PATCH 08/13] *: create a helper function to set the IP_MULTICAST_LOOP sockoption Signed-off-by: Renato Westphal --- ldpd/socket.c | 10 +--------- lib/sockopt.c | 15 ++++++++++++++- lib/sockopt.h | 2 ++ ospfd/ospf_network.c | 8 +++----- pimd/pim_sock.c | 4 ++-- pimd/pim_ssmpingd.c | 18 +++++++----------- zebra/irdp_packet.c | 8 ++------ 7 files changed, 31 insertions(+), 34 deletions(-) diff --git a/ldpd/socket.c b/ldpd/socket.c index cf352d7204..1bb0837401 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -421,15 +421,7 @@ sock_set_ipv4_mcast(struct iface *iface) int sock_set_ipv4_mcast_loop(int fd) { - uint8_t loop = 0; - - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - (char *)&loop, sizeof(loop)) < 0) { - log_warn("%s: error setting IP_MULTICAST_LOOP", __func__); - return (-1); - } - - return (0); + return (setsockopt_ipv4_multicast_loop(fd, 0)); } int diff --git a/lib/sockopt.c b/lib/sockopt.c index c480cee0d7..be3ac0e4bf 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -384,7 +384,20 @@ setsockopt_ipv4_multicast_if(int sock, struct in_addr if_addr, #error "Unsupported multicast API" #endif } - + +int +setsockopt_ipv4_multicast_loop (int sock, u_char val) +{ + int ret; + + ret = setsockopt (sock, IPPROTO_IP, IP_MULTICAST_LOOP, (void *) &val, + sizeof (val)); + if (ret < 0) + zlog_warn ("can't setsockopt IP_MULTICAST_LOOP"); + + return ret; +} + static int setsockopt_ipv4_ifindex (int sock, ifindex_t val) { diff --git a/lib/sockopt.h b/lib/sockopt.h index d67b510b66..02f0189345 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -89,6 +89,8 @@ extern int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr, unsigned int mcast_addr, ifindex_t ifindex); +extern int setsockopt_ipv4_multicast_loop (int sock, u_char val); + extern int setsockopt_ipv4_tos(int sock, int tos); /* Ask for, and get, ifindex, by whatever method is supported. */ diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 6caa38d68b..088123ea24 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -132,18 +132,16 @@ ospf_if_ipmulticast (struct ospf *top, struct prefix *p, ifindex_t ifindex) { u_char val; int ret, len; - - val = 0; - len = sizeof (val); - + /* Prevent receiving self-origined multicast packets. */ - ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, len); + ret = setsockopt_ipv4_multicast_loop (top->fd, 0); if (ret < 0) zlog_warn ("can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", top->fd, safe_strerror(errno)); /* Explicitly set multicast ttl to 1 -- endo. */ val = 1; + len = sizeof (val); ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); if (ret < 0) zlog_warn ("can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 90b11dcfd6..231efd0f22 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -35,6 +35,7 @@ #include "privs.h" #include "if.h" #include "vrf.h" +#include "sockopt.h" #include "pimd.h" #include "pim_mroute.h" @@ -173,8 +174,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char lo } } - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - (void *) &loop, sizeof(loop))) { + if (setsockopt_ipv4_multicast_loop (fd, loop)) { zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", loop ? "enable" : "disable", fd, errno, safe_strerror(errno)); diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index daa3c65e36..fba563a6f8 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -25,6 +25,7 @@ #include "if.h" #include "log.h" #include "memory.h" +#include "sockopt.h" #include "pim_ssmpingd.h" #include "pim_time.h" @@ -150,17 +151,12 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) return -1; } - { - u_char loop = 0; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - (void *) &loop, sizeof(loop))) { - zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, - loop ? "enable" : "disable", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_LOOP; - } + if (setsockopt_ipv4_multicast_loop (fd, 0)) { + zlog_warn("%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_LOOP; } if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 290a6180e7..c9c32ce311 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -323,12 +323,8 @@ send_packet(struct interface *ifp, zlog_warn("sendto %s", safe_strerror (errno)); } - if(dst != INADDR_BROADCAST) { - on = 0; - if( setsockopt(irdp_sock,IPPROTO_IP, IP_MULTICAST_LOOP, - (char *)&on,sizeof(on)) < 0) - zlog_warn("sendto %s", safe_strerror (errno)); - } + if(dst != INADDR_BROADCAST) + setsockopt_ipv4_multicast_loop (irdp_sock, 0); memset(&sockdst,0,sizeof(sockdst)); sockdst.sin_family=AF_INET; From 043144ea897ba6a9d1e5f1a8d9e0d623d78bc2a4 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 12 Nov 2016 19:11:13 -0200 Subject: [PATCH 09/13] ripd: make use of the IP_MULTICAST_LOOP sockoption We still need to check for self-generated packets on rip_read() because ripd may also send broadcast packets. But using IP_MULTICAST_LOOP on the ripd socket will at least prevent us from receiving a lot unnecessary multicast packets when RIPv2 is being used, thus improving performance. Signed-off-by: Renato Westphal --- ripd/ripd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ripd/ripd.c b/ripd/ripd.c index a83c50c7ce..c2872d937a 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1365,6 +1365,7 @@ rip_create_socket (void) sockopt_broadcast (sock); sockopt_reuseaddr (sock); sockopt_reuseport (sock); + setsockopt_ipv4_multicast_loop (sock, 0); #ifdef RIP_RECVMSG setsockopt_pktinfo (sock); #endif /* RIP_RECVMSG */ From 8578874d6922538fd7f13dce74db488569446993 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Sat, 12 Nov 2016 19:34:37 -0200 Subject: [PATCH 10/13] ripd: minor code simplification * Simplify the RIP_TIMER_OFF macro and use it on more places; * Be more explicit when creating the RIP UDP socket - cosmetic change since socket(AF_INET,SOCK_DGRAM,0) defaults to UDP on every known UNIX/Linux platform. Signed-off-by: Renato Westphal --- ripd/ripd.c | 26 +++++--------------------- ripd/ripd.h | 9 +-------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index c2872d937a..e9d610cea7 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1355,7 +1355,7 @@ rip_create_socket (void) addr.sin_port = htons (RIP_PORT_DEFAULT); /* Make datagram socket. */ - sock = socket (AF_INET, SOCK_DGRAM, 0); + sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { zlog_err("Cannot create UDP socket: %s", safe_strerror(errno)); @@ -2548,11 +2548,7 @@ rip_update (struct thread *t) /* Triggered updates may be suppressed if a regular update is due by the time the triggered update would be sent. */ - if (rip->t_triggered_interval) - { - thread_cancel (rip->t_triggered_interval); - rip->t_triggered_interval = NULL; - } + RIP_TIMER_OFF (rip->t_triggered_interval); rip->trigger = 0; /* Register myself. */ @@ -2606,11 +2602,7 @@ rip_triggered_update (struct thread *t) rip->t_triggered_update = NULL; /* Cancel interval timer. */ - if (rip->t_triggered_interval) - { - thread_cancel (rip->t_triggered_interval); - rip->t_triggered_interval = NULL; - } + RIP_TIMER_OFF (rip->t_triggered_interval); rip->trigger = 0; /* Logging triggered update. */ @@ -2788,11 +2780,7 @@ rip_event (enum rip_event event, int sock) rip->t_read = thread_add_read (master, rip_read, NULL, sock); break; case RIP_UPDATE_EVENT: - if (rip->t_update) - { - thread_cancel (rip->t_update); - rip->t_update = NULL; - } + RIP_TIMER_OFF (rip->t_update); jitter = rip_update_jitter (rip->update_time); rip->t_update = thread_add_timer (master, rip_update, NULL, @@ -3887,11 +3875,7 @@ rip_clean (void) RIP_TIMER_OFF (rip->t_triggered_interval); /* Cancel read thread. */ - if (rip->t_read) - { - thread_cancel (rip->t_read); - rip->t_read = NULL; - } + THREAD_READ_OFF (rip->t_read); /* Close RIP socket. */ if (rip->sock >= 0) diff --git a/ripd/ripd.h b/ripd/ripd.h index b26a1d234c..cb4764a7f1 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -373,14 +373,7 @@ enum rip_event } while (0) /* Macro for timer turn off. */ -#define RIP_TIMER_OFF(X) \ - do { \ - if (X) \ - { \ - thread_cancel (X); \ - (X) = NULL; \ - } \ - } while (0) +#define RIP_TIMER_OFF(X) THREAD_TIMER_OFF(X) /* Prototypes. */ extern void rip_init (void); From 705e86f06eaea9676099f5a32cbed5599ae474c6 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Wed, 16 Nov 2016 16:14:45 -0200 Subject: [PATCH 11/13] ripngd: implement optional heuristic suggested by RFC 2080 RFC 2080 - Section 2.4.2: "If the new metric is the same as the old one, examine the timeout for the existing route. If it is at least halfway to the expiration point, switch to the new route. This heuristic is optional, but highly recommended". Implement this optional heuristic only when ECMP is disabled globally ("no allow-ecmp"), otherwise all routes with the same metric should be used. Fixes IxANVL RIPng test 7.21. Signed-off-by: Renato Westphal --- ripngd/ripngd.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 2a32b934f2..96206e2b08 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -829,8 +829,10 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, * with the new one in below. */ break; - /* Metrics are same. Keep "rinfo" null and the new route - * is added in the ECMP list in below. */ + /* Metrics are same. Unless ECMP is disabled, keep "rinfo" null and + * the new route is added in the ECMP list in below. */ + if (! ripng->ecmp) + break; } } @@ -874,11 +876,24 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) && (rinfo->ifindex == ifp->ifindex)); + /* + * RFC 2080 - Section 2.4.2: + * "If the new metric is the same as the old one, examine the timeout + * for the existing route. If it is at least halfway to the expiration + * point, switch to the new route. This heuristic is optional, but + * highly recommended". + */ + if (!ripng->ecmp && !same && + rinfo->metric == rte->metric && rinfo->t_timeout && + (thread_timer_remain_second (rinfo->t_timeout) < (ripng->timeout_time / 2))) + { + ripng_ecmp_replace (&newinfo); + } /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different than the old one; or, if the new metric is lower than the old one; do the following actions: */ - if ((same && rinfo->metric != rte->metric) || + else if ((same && rinfo->metric != rte->metric) || rte->metric < rinfo->metric) { if (listcount (list) == 1) From d7f966abed5f822e6f90442de5ccbf848be01b64 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 17 Nov 2016 16:33:09 -0200 Subject: [PATCH 12/13] ripngd: implement the "clear ipv6 ripng" vty command This command deletes all received routes from the RIPng routing table. It should be used with caution as it can create black holes in the network (until it reconverges). Very useful to make automated testing (e.g. ANVL) more predictable. Signed-off-by: Renato Westphal --- ripngd/ripngd.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 96206e2b08..82c4877485 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2170,6 +2170,54 @@ DEFUN (show_ipv6_ripng_status, return CMD_SUCCESS; } +DEFUN (clear_ipv6_rip, + clear_ipv6_rip_cmd, + "clear ipv6 ripng", + CLEAR_STR + IPV6_STR + "Clear IPv6 RIP database") +{ + struct route_node *rp; + struct ripng_info *rinfo; + struct list *list; + struct listnode *listnode; + + /* Clear received RIPng routes */ + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + { + list = rp->info; + if (list == NULL) + continue; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + if (! ripng_route_rte (rinfo)) + continue; + + if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete (rp); + break; + } + + if (rinfo) + { + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + ripng_info_free (rinfo); + } + + if (list_isempty (list)) + { + list_free (list); + rp->info = NULL; + route_unlock_node (rp); + } + } + + return CMD_SUCCESS; +} + DEFUN (router_ripng, router_ripng_cmd, "router ripng", @@ -3047,6 +3095,8 @@ ripng_init () install_element (VIEW_NODE, &show_ipv6_ripng_cmd); install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd); + install_element (ENABLE_NODE, &clear_ipv6_rip_cmd); + install_element (CONFIG_NODE, &router_ripng_cmd); install_element (CONFIG_NODE, &no_router_ripng_cmd); From 9e544a9c4bd13aac6bbeaff235abaaeb07a186b3 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 18 Nov 2016 15:39:25 -0200 Subject: [PATCH 13/13] ripngd: fix drop of multicast membership when the interface is down When an interface is shut down, ripng_multicast_leave() is called after ifp->flags is updated in ripng_interface_down(). So we shouldn't check if the interface is up in order to proceed with the membership drop. For consistency's sake, don't check for if_is_up() in ripng_multicast_join() as well. In this case, this function is only called when the interface is up, so the check was unnecessary. Signed-off-by: Renato Westphal --- ripngd/ripng_interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index c4dec7e7b1..a8742ec9a4 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -65,7 +65,7 @@ ripng_multicast_join (struct interface *ifp) struct ipv6_mreq mreq; int save_errno; - if (if_is_up (ifp) && if_is_multicast (ifp)) { + if (if_is_multicast (ifp)) { memset (&mreq, 0, sizeof (mreq)); inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = ifp->ifindex; @@ -116,7 +116,7 @@ ripng_multicast_leave (struct interface *ifp) int ret; struct ipv6_mreq mreq; - if (if_is_up (ifp) && if_is_multicast (ifp)) { + if (if_is_multicast (ifp)) { memset (&mreq, 0, sizeof (mreq)); inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = ifp->ifindex;