Here's a patch to address issues raised in [quagga-dev 490] and
re-opened in [quagga-dev 669]. I've tested this successfully
on linux and found all results satisfactory.
The changes are:
- rip_interface.c: obsolete unbind code in rip_interface_multicast_set,
and instead do the more portable (though slower) method of creating a
socket for each outgoing packet and binding the source address on the
new socket.
- rip_interface.c, ripd.c, ripd.h: Modify rip_request_send so that source
address is determined by the caller of rip_request_send for
ripv1 packets and non-multicast interfaces (rip_request_send loops
over all connected address in all other cases).
- ripd.c/rip_send_packet: don't send packets with source set to
ZEBRA_IFA_SECONDARY connected addresses; improved debug messages;
I've also fixed indenting as appropriate, though (on Paul's request)
the -uwb flags to rcsdiff doesn't list all the white-space fixes.
--Sowmini
===================================================================
RCS file: rip_interface.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -uwb -r1.13 -r1.14
--- /tmp/T0P.a9ej Wed Jan 21 12:26:45 2004
+++ /tmp/T1Q.a9ej Wed Jan 21 12:26:46 2004
@@ -146,16 +146,20 @@
struct in_addr addr;
struct prefix_ipv4 *p;
+ if (connected != NULL)
+ {
if (if_pointopoint)
p = (struct prefix_ipv4 *) connected->destination;
else
p = (struct prefix_ipv4 *) connected->address;
-
addr = p->prefix;
+ }
+ else
+ {
+ addr.s_addr = INADDR_ANY;
+ }
-
- if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF,
- addr, 0, 0) < 0)
+ if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF, addr, 0, 0) < 0)
{
zlog_warn ("Can't setsockopt IP_MULTICAST_IF to fd %d", sock);
return;
@@ -171,8 +175,9 @@
else
from.sin_port = htons (RIP_PORT_DEFAULT);
- /* Address shoud be any address. */
+ /* Address should be any address. */
from.sin_family = AF_INET;
+ if (connected)
addr = ((struct prefix_ipv4 *) connected->address)->prefix;
from.sin_addr = addr;
#ifdef HAVE_SIN_LEN
@@ -182,7 +187,6 @@
if (ripd_privs.change (ZPRIVS_RAISE))
zlog_err ("rip_interface_multicast_set: could not raise privs");
- bind (sock, NULL, 0); /* unbind any previous association */
ret = bind (sock, (struct sockaddr *) & from, sizeof (struct sockaddr_in));
if (ret < 0)
{
@@ -209,7 +213,7 @@
if (IS_RIP_DEBUG_EVENT)
zlog_info ("multicast request on %s", ifp->name);
- rip_request_send (NULL, ifp, version);
+ rip_request_send (NULL, ifp, version, NULL);
return;
}
@@ -238,7 +242,7 @@
if (IS_RIP_DEBUG_EVENT)
zlog_info ("SEND request to %s", inet_ntoa (to.sin_addr));
- rip_request_send (&to, ifp, version);
+ rip_request_send (&to, ifp, version, connected);
}
}
}
@@ -284,7 +288,7 @@
to.sin_port = htons (RIP_PORT_DEFAULT);
to.sin_addr = addr;
- rip_request_send (&to, NULL, rip->version_send);
+ rip_request_send (&to, NULL, rip->version_send, NULL);
}
/* Request routes at all interfaces. */
===================================================================
RCS file: ripd.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -uwb -r1.11 -r1.12
--- /tmp/T0DVG.ej Wed Jan 21 12:27:05 2004
+++ /tmp/T1EVG.ej Wed Jan 21 12:27:05 2004
@@ -61,7 +61,7 @@
void rip_output_process (struct interface *, struct prefix *,
struct sockaddr_in *, int, u_char,
- struct prefix_ipv4 *);
+ struct connected *, struct prefix_ipv4 *);
/* RIP output routes type. */
enum
@@ -1235,11 +1235,33 @@
/* RIP packet send to destination address. */
int
rip_send_packet (caddr_t buf, int size, struct sockaddr_in *to,
- struct interface *ifp)
+ struct interface *ifp, struct connected *connected)
{
- int ret;
+ int ret, send_sock;
struct sockaddr_in sin;
+ if (IS_RIP_DEBUG_PACKET)
+ {
+ char dst[20];
+ if (to)
+ {
+ strcpy(dst, inet_ntoa(to->sin_addr));
+ }
+ else
+ {
+ sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
+ strcpy(dst, inet_ntoa(sin.sin_addr));
+ }
+ zlog_info("rip_send_packet %s > %s (%s)",
+ inet_ntoa(connected->address->u.prefix4), dst, ifp->name);
+ }
+ if (connected->flags & ZEBRA_IFA_SECONDARY)
+ {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_info("duplicate dropped");
+ return 0;
+ }
+
/* Make destination address. */
memset (&sin, 0, sizeof (struct sockaddr_in));
sin.sin_family = AF_INET;
@@ -1252,6 +1274,7 @@
{
sin.sin_port = to->sin_port;
sin.sin_addr = to->sin_addr;
+ send_sock = rip->sock;
}
else
{
@@ -1259,11 +1282,28 @@
sin.sin_port = htons (RIP_PORT_DEFAULT);
sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
- /* caller has set multicast interface */
-
+ /*
+ * 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.
+ */
+ send_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (send_sock < 0)
+ {
+ zlog_warn("rip_send_packet could not create socket %s",
+ strerror(errno));
+ return -1;
}
+ sockopt_broadcast (send_sock);
+ sockopt_reuseaddr (send_sock);
+ sockopt_reuseport (send_sock);
+#ifdef RIP_RECVMSG
+ setsockopt_pktinfo (send_sock);
+#endif /* RIP_RECVMSG */
+ rip_interface_multicast_set(send_sock, connected, if_is_pointopoint(ifp));
+ }
- ret = sendto (rip->sock, buf, size, 0, (struct sockaddr *)&sin,
+ ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin,
sizeof (struct sockaddr_in));
if (IS_RIP_DEBUG_EVENT)
@@ -1273,6 +1313,9 @@
if (ret < 0)
zlog_warn ("can't send packet : %s", strerror (errno));
+ if (!to)
+ close(send_sock);
+
return ret;
}
@@ -1456,7 +1499,7 @@
/* All route with split horizon */
rip_output_process (ifp, NULL, from, rip_all_route, packet->version,
- &saddr);
+ NULL, &saddr);
}
else
{
@@ -1488,7 +1531,7 @@
}
packet->command = RIP_RESPONSE;
- rip_send_packet ((caddr_t) packet, size, from, ifp);
+ rip_send_packet ((caddr_t) packet, size, from, ifp, NULL);
}
rip_global_queries++;
}
@@ -1882,6 +1925,8 @@
if (ret < 0)
{
perror ("bind");
+ if (ripd_privs.change (ZPRIVS_LOWER))
+ zlog_err ("rip_create_socket: could not lower privs");
return ret;
}
if (ripd_privs.change (ZPRIVS_LOWER))
@@ -1981,7 +2026,7 @@
void
rip_output_process (struct interface *ifp, struct prefix *ifaddr,
struct sockaddr_in *to, int route_type, u_char version,
- struct prefix_ipv4 *saddr)
+ struct connected *connected, struct prefix_ipv4 *saddr)
{
int ret;
struct stream *s;
@@ -2117,9 +2162,20 @@
/* if (split_horizon == rip_split_horizon) */
if (ri->split_horizon == RIP_SPLIT_HORIZON)
{
- /* We perform split horizon for RIP and connected route. */
- if ((rinfo->type == ZEBRA_ROUTE_RIP ||
- rinfo->type == ZEBRA_ROUTE_CONNECT) &&
+ /*
+ * We perform split horizon for RIP and connected route.
+ * For rip routes, we want to suppress the route if we would
+ * end up sending the route back on the interface that we
+ * learned it from, with a higher metric. For connected routes,
+ * we suppress the route if the prefix is a subset of the
+ * source address that we are going to use for the packet
+ * (in order to handle the case when multiple subnets are
+ * configured on the same interface).
+ */
+ if (rinfo->type == ZEBRA_ROUTE_RIP &&
+ rinfo->ifindex == ifp->ifindex)
+ continue;
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
prefix_match((struct prefix *)p, (struct prefix *)saddr))
continue;
}
@@ -2204,10 +2260,22 @@
* for RIP and connected routes.
**/
if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
- if ((rinfo->type == ZEBRA_ROUTE_RIP ||
- rinfo->type == ZEBRA_ROUTE_CONNECT) &&
+ /*
+ * We perform split horizon for RIP and connected route.
+ * For rip routes, we want to suppress the route if we would
+ * end up sending the route back on the interface that we
+ * learned it from, with a higher metric. For connected routes,
+ * we suppress the route if the prefix is a subset of the
+ * source address that we are going to use for the packet
+ * (in order to handle the case when multiple subnets are
+ * configured on the same interface).
+ */
+ if (rinfo->type == ZEBRA_ROUTE_RIP &&
rinfo->ifindex == ifp->ifindex)
rinfo->metric_out = RIP_METRIC_INFINITY;
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
+ prefix_match((struct prefix *)p, (struct prefix *)saddr))
+ rinfo->metric_out = RIP_METRIC_INFINITY;
}
/* Write RTE to the stream. */
@@ -2218,7 +2286,7 @@
rip_auth_md5_set (s, ifp);
ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s),
- to, ifp);
+ to, ifp, connected);
if (ret >= 0 && IS_RIP_DEBUG_SEND)
rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
@@ -2234,7 +2302,8 @@
if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
rip_auth_md5_set (s, ifp);
- ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifp);
+ ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifp,
+ connected);
if (ret >= 0 && IS_RIP_DEBUG_SEND)
rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
@@ -2250,12 +2319,13 @@
/* Send RIP packet to the interface. */
void
rip_update_interface (struct interface *ifp, u_char version, int route_type,
- struct prefix_ipv4 *saddr)
+ struct connected *sconn)
{
struct prefix_ipv4 *p;
struct connected *connected;
listnode node;
struct sockaddr_in to;
+ struct prefix_ipv4 *saddr = (struct prefix_ipv4 *) sconn->address;
/* When RIP version is 2 and multicast enable interface. */
if (version == RIPv2 && if_is_multicast (ifp))
@@ -2264,7 +2334,7 @@
zlog_info ("multicast announce on %s ", ifp->name);
rip_output_process (ifp, NULL, NULL, route_type, rip->version_send,
- saddr);
+ sconn, saddr);
return;
}
@@ -2292,7 +2362,7 @@
inet_ntoa (to.sin_addr), ifp->name);
rip_output_process (ifp, connected->address, &to, route_type,
- rip->version_send, saddr);
+ rip->version_send, connected, saddr);
}
}
}
@@ -2346,10 +2416,11 @@
LIST_LOOP(ifp->connected, connected, ifnode)
{
struct prefix_ipv4 *ifaddr;
-
-
- /* If there is no version configuration in the interface,
- use rip's version setting. */
+ int done = 0;
+ /*
+ * If there is no version configuration in the interface,
+ * use rip's version setting.
+ */
int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
rip->version_send : ri->ri_send);
@@ -2358,12 +2429,14 @@
if (ifaddr->family != AF_INET)
continue;
- rip_interface_multicast_set(rip->sock, connected,
- if_is_pointopoint(ifp));
- if (vsend & RIPv1)
- rip_update_interface (ifp, RIPv1, route_type, ifaddr);
- if (vsend & RIPv2)
- rip_update_interface (ifp, RIPv2, route_type, ifaddr);
+ if ((vsend & RIPv1) && !done)
+ rip_update_interface (ifp, RIPv1, route_type, connected);
+ if ((vsend & RIPv2) && if_is_multicast(ifp))
+ rip_update_interface (ifp, RIPv2, route_type, connected);
+ done = 1;
+ if (!(vsend & RIPv2) || !if_is_multicast(ifp))
+ break;
+
}
}
}
@@ -2388,7 +2461,8 @@
to.sin_port = htons (RIP_PORT_DEFAULT);
/* RIP version is rip's configuration. */
- rip_output_process (ifp, NULL, &to, route_type, rip->version_send, p);
+ rip_output_process (ifp, NULL, &to, route_type, rip->version_send,
+ NULL, p);
}
}
@@ -2564,12 +2638,11 @@
/* Sned RIP request to the destination. */
int
rip_request_send (struct sockaddr_in *to, struct interface *ifp,
- u_char version)
+ u_char version, struct connected *connected)
{
struct rte *rte;
struct rip_packet rip_packet;
listnode node;
- struct connected *connected;
memset (&rip_packet, 0, sizeof (rip_packet));
@@ -2578,6 +2651,20 @@
rte = rip_packet.rte;
rte->metric = htonl (RIP_METRIC_INFINITY);
+ if (connected)
+ {
+ /*
+ * connected is only sent for ripv1 case, or when
+ * interface does not support multicast. Caller loops
+ * over each connected address for this case.
+ */
+ if (rip_send_packet ((caddr_t) &rip_packet, sizeof (rip_packet),
+ to, ifp, connected) != sizeof (rip_packet))
+ return -1;
+ else
+ return sizeof (rip_packet);
+ }
+
/* send request on each connected network */
LIST_LOOP(ifp->connected, connected, node)
{
@@ -2588,10 +2675,8 @@
if (p->family != AF_INET)
continue;
- rip_interface_multicast_set(rip->sock, connected,
- if_is_pointopoint(ifp));
if (rip_send_packet ((caddr_t) &rip_packet, sizeof (rip_packet),
- to, ifp) != sizeof (rip_packet))
+ to, ifp, connected) != sizeof (rip_packet))
return -1;
}
return sizeof (rip_packet);
===================================================================
RCS file: ripd.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -uwb -r1.8 -r1.9
--- /tmp/T0a4aafj Wed Jan 21 12:27:22 2004
+++ /tmp/T1b4aafj Wed Jan 21 12:27:22 2004
@@ -377,7 +377,8 @@
int if_check_address (struct in_addr addr);
int if_valid_neighbor (struct in_addr addr);
-int rip_request_send (struct sockaddr_in *, struct interface *, u_char);
+int rip_request_send (struct sockaddr_in *, struct interface *, u_char,
+ struct connected *);
int rip_neighbor_lookup (struct sockaddr_in *);
void rip_redistribute_add (int, int, struct prefix_ipv4 *, unsigned int,
struct in_addr *);
re-opened in [quagga-dev 669]. I've tested this successfully
on linux and found all results satisfactory.
The changes are:
- rip_interface.c: obsolete unbind code in rip_interface_multicast_set,
and instead do the more portable (though slower) method of creating a
socket for each outgoing packet and binding the source address on the
new socket.
- rip_interface.c, ripd.c, ripd.h: Modify rip_request_send so that source
address is determined by the caller of rip_request_send for
ripv1 packets and non-multicast interfaces (rip_request_send loops
over all connected address in all other cases).
- ripd.c/rip_send_packet: don't send packets with source set to
ZEBRA_IFA_SECONDARY connected addresses; improved debug messages;
I've also fixed indenting as appropriate, though (on Paul's request)
the -uwb flags to rcsdiff doesn't list all the white-space fixes.
--Sowmini
===================================================================
RCS file: rip_interface.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -uwb -r1.13 -r1.14
--- /tmp/T0P.a9ej Wed Jan 21 12:26:45 2004
+++ /tmp/T1Q.a9ej Wed Jan 21 12:26:46 2004
@@ -146,16 +146,20 @@
struct in_addr addr;
struct prefix_ipv4 *p;
+ if (connected != NULL)
+ {
if (if_pointopoint)
p = (struct prefix_ipv4 *) connected->destination;
else
p = (struct prefix_ipv4 *) connected->address;
-
addr = p->prefix;
+ }
+ else
+ {
+ addr.s_addr = INADDR_ANY;
+ }
-
- if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF,
- addr, 0, 0) < 0)
+ if (setsockopt_multicast_ipv4 (sock, IP_MULTICAST_IF, addr, 0, 0) < 0)
{
zlog_warn ("Can't setsockopt IP_MULTICAST_IF to fd %d", sock);
return;
@@ -171,8 +175,9 @@
else
from.sin_port = htons (RIP_PORT_DEFAULT);
- /* Address shoud be any address. */
+ /* Address should be any address. */
from.sin_family = AF_INET;
+ if (connected)
addr = ((struct prefix_ipv4 *) connected->address)->prefix;
from.sin_addr = addr;
#ifdef HAVE_SIN_LEN
@@ -182,7 +187,6 @@
if (ripd_privs.change (ZPRIVS_RAISE))
zlog_err ("rip_interface_multicast_set: could not raise privs");
- bind (sock, NULL, 0); /* unbind any previous association */
ret = bind (sock, (struct sockaddr *) & from, sizeof (struct sockaddr_in));
if (ret < 0)
{
@@ -209,7 +213,7 @@
if (IS_RIP_DEBUG_EVENT)
zlog_info ("multicast request on %s", ifp->name);
- rip_request_send (NULL, ifp, version);
+ rip_request_send (NULL, ifp, version, NULL);
return;
}
@@ -238,7 +242,7 @@
if (IS_RIP_DEBUG_EVENT)
zlog_info ("SEND request to %s", inet_ntoa (to.sin_addr));
- rip_request_send (&to, ifp, version);
+ rip_request_send (&to, ifp, version, connected);
}
}
}
@@ -284,7 +288,7 @@
to.sin_port = htons (RIP_PORT_DEFAULT);
to.sin_addr = addr;
- rip_request_send (&to, NULL, rip->version_send);
+ rip_request_send (&to, NULL, rip->version_send, NULL);
}
/* Request routes at all interfaces. */
===================================================================
RCS file: ripd.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -uwb -r1.11 -r1.12
--- /tmp/T0DVG.ej Wed Jan 21 12:27:05 2004
+++ /tmp/T1EVG.ej Wed Jan 21 12:27:05 2004
@@ -61,7 +61,7 @@
void rip_output_process (struct interface *, struct prefix *,
struct sockaddr_in *, int, u_char,
- struct prefix_ipv4 *);
+ struct connected *, struct prefix_ipv4 *);
/* RIP output routes type. */
enum
@@ -1235,11 +1235,33 @@
/* RIP packet send to destination address. */
int
rip_send_packet (caddr_t buf, int size, struct sockaddr_in *to,
- struct interface *ifp)
+ struct interface *ifp, struct connected *connected)
{
- int ret;
+ int ret, send_sock;
struct sockaddr_in sin;
+ if (IS_RIP_DEBUG_PACKET)
+ {
+ char dst[20];
+ if (to)
+ {
+ strcpy(dst, inet_ntoa(to->sin_addr));
+ }
+ else
+ {
+ sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
+ strcpy(dst, inet_ntoa(sin.sin_addr));
+ }
+ zlog_info("rip_send_packet %s > %s (%s)",
+ inet_ntoa(connected->address->u.prefix4), dst, ifp->name);
+ }
+ if (connected->flags & ZEBRA_IFA_SECONDARY)
+ {
+ if (IS_RIP_DEBUG_PACKET)
+ zlog_info("duplicate dropped");
+ return 0;
+ }
+
/* Make destination address. */
memset (&sin, 0, sizeof (struct sockaddr_in));
sin.sin_family = AF_INET;
@@ -1252,6 +1274,7 @@
{
sin.sin_port = to->sin_port;
sin.sin_addr = to->sin_addr;
+ send_sock = rip->sock;
}
else
{
@@ -1259,11 +1282,28 @@
sin.sin_port = htons (RIP_PORT_DEFAULT);
sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP);
- /* caller has set multicast interface */
-
+ /*
+ * 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.
+ */
+ send_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (send_sock < 0)
+ {
+ zlog_warn("rip_send_packet could not create socket %s",
+ strerror(errno));
+ return -1;
}
+ sockopt_broadcast (send_sock);
+ sockopt_reuseaddr (send_sock);
+ sockopt_reuseport (send_sock);
+#ifdef RIP_RECVMSG
+ setsockopt_pktinfo (send_sock);
+#endif /* RIP_RECVMSG */
+ rip_interface_multicast_set(send_sock, connected, if_is_pointopoint(ifp));
+ }
- ret = sendto (rip->sock, buf, size, 0, (struct sockaddr *)&sin,
+ ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin,
sizeof (struct sockaddr_in));
if (IS_RIP_DEBUG_EVENT)
@@ -1273,6 +1313,9 @@
if (ret < 0)
zlog_warn ("can't send packet : %s", strerror (errno));
+ if (!to)
+ close(send_sock);
+
return ret;
}
@@ -1456,7 +1499,7 @@
/* All route with split horizon */
rip_output_process (ifp, NULL, from, rip_all_route, packet->version,
- &saddr);
+ NULL, &saddr);
}
else
{
@@ -1488,7 +1531,7 @@
}
packet->command = RIP_RESPONSE;
- rip_send_packet ((caddr_t) packet, size, from, ifp);
+ rip_send_packet ((caddr_t) packet, size, from, ifp, NULL);
}
rip_global_queries++;
}
@@ -1882,6 +1925,8 @@
if (ret < 0)
{
perror ("bind");
+ if (ripd_privs.change (ZPRIVS_LOWER))
+ zlog_err ("rip_create_socket: could not lower privs");
return ret;
}
if (ripd_privs.change (ZPRIVS_LOWER))
@@ -1981,7 +2026,7 @@
void
rip_output_process (struct interface *ifp, struct prefix *ifaddr,
struct sockaddr_in *to, int route_type, u_char version,
- struct prefix_ipv4 *saddr)
+ struct connected *connected, struct prefix_ipv4 *saddr)
{
int ret;
struct stream *s;
@@ -2117,9 +2162,20 @@
/* if (split_horizon == rip_split_horizon) */
if (ri->split_horizon == RIP_SPLIT_HORIZON)
{
- /* We perform split horizon for RIP and connected route. */
- if ((rinfo->type == ZEBRA_ROUTE_RIP ||
- rinfo->type == ZEBRA_ROUTE_CONNECT) &&
+ /*
+ * We perform split horizon for RIP and connected route.
+ * For rip routes, we want to suppress the route if we would
+ * end up sending the route back on the interface that we
+ * learned it from, with a higher metric. For connected routes,
+ * we suppress the route if the prefix is a subset of the
+ * source address that we are going to use for the packet
+ * (in order to handle the case when multiple subnets are
+ * configured on the same interface).
+ */
+ if (rinfo->type == ZEBRA_ROUTE_RIP &&
+ rinfo->ifindex == ifp->ifindex)
+ continue;
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
prefix_match((struct prefix *)p, (struct prefix *)saddr))
continue;
}
@@ -2204,10 +2260,22 @@
* for RIP and connected routes.
**/
if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) {
- if ((rinfo->type == ZEBRA_ROUTE_RIP ||
- rinfo->type == ZEBRA_ROUTE_CONNECT) &&
+ /*
+ * We perform split horizon for RIP and connected route.
+ * For rip routes, we want to suppress the route if we would
+ * end up sending the route back on the interface that we
+ * learned it from, with a higher metric. For connected routes,
+ * we suppress the route if the prefix is a subset of the
+ * source address that we are going to use for the packet
+ * (in order to handle the case when multiple subnets are
+ * configured on the same interface).
+ */
+ if (rinfo->type == ZEBRA_ROUTE_RIP &&
rinfo->ifindex == ifp->ifindex)
rinfo->metric_out = RIP_METRIC_INFINITY;
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT &&
+ prefix_match((struct prefix *)p, (struct prefix *)saddr))
+ rinfo->metric_out = RIP_METRIC_INFINITY;
}
/* Write RTE to the stream. */
@@ -2218,7 +2286,7 @@
rip_auth_md5_set (s, ifp);
ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s),
- to, ifp);
+ to, ifp, connected);
if (ret >= 0 && IS_RIP_DEBUG_SEND)
rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
@@ -2234,7 +2302,8 @@
if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5)
rip_auth_md5_set (s, ifp);
- ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifp);
+ ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifp,
+ connected);
if (ret >= 0 && IS_RIP_DEBUG_SEND)
rip_packet_dump ((struct rip_packet *)STREAM_DATA (s),
@@ -2250,12 +2319,13 @@
/* Send RIP packet to the interface. */
void
rip_update_interface (struct interface *ifp, u_char version, int route_type,
- struct prefix_ipv4 *saddr)
+ struct connected *sconn)
{
struct prefix_ipv4 *p;
struct connected *connected;
listnode node;
struct sockaddr_in to;
+ struct prefix_ipv4 *saddr = (struct prefix_ipv4 *) sconn->address;
/* When RIP version is 2 and multicast enable interface. */
if (version == RIPv2 && if_is_multicast (ifp))
@@ -2264,7 +2334,7 @@
zlog_info ("multicast announce on %s ", ifp->name);
rip_output_process (ifp, NULL, NULL, route_type, rip->version_send,
- saddr);
+ sconn, saddr);
return;
}
@@ -2292,7 +2362,7 @@
inet_ntoa (to.sin_addr), ifp->name);
rip_output_process (ifp, connected->address, &to, route_type,
- rip->version_send, saddr);
+ rip->version_send, connected, saddr);
}
}
}
@@ -2346,10 +2416,11 @@
LIST_LOOP(ifp->connected, connected, ifnode)
{
struct prefix_ipv4 *ifaddr;
-
-
- /* If there is no version configuration in the interface,
- use rip's version setting. */
+ int done = 0;
+ /*
+ * If there is no version configuration in the interface,
+ * use rip's version setting.
+ */
int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ?
rip->version_send : ri->ri_send);
@@ -2358,12 +2429,14 @@
if (ifaddr->family != AF_INET)
continue;
- rip_interface_multicast_set(rip->sock, connected,
- if_is_pointopoint(ifp));
- if (vsend & RIPv1)
- rip_update_interface (ifp, RIPv1, route_type, ifaddr);
- if (vsend & RIPv2)
- rip_update_interface (ifp, RIPv2, route_type, ifaddr);
+ if ((vsend & RIPv1) && !done)
+ rip_update_interface (ifp, RIPv1, route_type, connected);
+ if ((vsend & RIPv2) && if_is_multicast(ifp))
+ rip_update_interface (ifp, RIPv2, route_type, connected);
+ done = 1;
+ if (!(vsend & RIPv2) || !if_is_multicast(ifp))
+ break;
+
}
}
}
@@ -2388,7 +2461,8 @@
to.sin_port = htons (RIP_PORT_DEFAULT);
/* RIP version is rip's configuration. */
- rip_output_process (ifp, NULL, &to, route_type, rip->version_send, p);
+ rip_output_process (ifp, NULL, &to, route_type, rip->version_send,
+ NULL, p);
}
}
@@ -2564,12 +2638,11 @@
/* Sned RIP request to the destination. */
int
rip_request_send (struct sockaddr_in *to, struct interface *ifp,
- u_char version)
+ u_char version, struct connected *connected)
{
struct rte *rte;
struct rip_packet rip_packet;
listnode node;
- struct connected *connected;
memset (&rip_packet, 0, sizeof (rip_packet));
@@ -2578,6 +2651,20 @@
rte = rip_packet.rte;
rte->metric = htonl (RIP_METRIC_INFINITY);
+ if (connected)
+ {
+ /*
+ * connected is only sent for ripv1 case, or when
+ * interface does not support multicast. Caller loops
+ * over each connected address for this case.
+ */
+ if (rip_send_packet ((caddr_t) &rip_packet, sizeof (rip_packet),
+ to, ifp, connected) != sizeof (rip_packet))
+ return -1;
+ else
+ return sizeof (rip_packet);
+ }
+
/* send request on each connected network */
LIST_LOOP(ifp->connected, connected, node)
{
@@ -2588,10 +2675,8 @@
if (p->family != AF_INET)
continue;
- rip_interface_multicast_set(rip->sock, connected,
- if_is_pointopoint(ifp));
if (rip_send_packet ((caddr_t) &rip_packet, sizeof (rip_packet),
- to, ifp) != sizeof (rip_packet))
+ to, ifp, connected) != sizeof (rip_packet))
return -1;
}
return sizeof (rip_packet);
===================================================================
RCS file: ripd.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -uwb -r1.8 -r1.9
--- /tmp/T0a4aafj Wed Jan 21 12:27:22 2004
+++ /tmp/T1b4aafj Wed Jan 21 12:27:22 2004
@@ -377,7 +377,8 @@
int if_check_address (struct in_addr addr);
int if_valid_neighbor (struct in_addr addr);
-int rip_request_send (struct sockaddr_in *, struct interface *, u_char);
+int rip_request_send (struct sockaddr_in *, struct interface *, u_char,
+ struct connected *);
int rip_neighbor_lookup (struct sockaddr_in *);
void rip_redistribute_add (int, int, struct prefix_ipv4 *, unsigned int,
struct in_addr *);