diff options
-rw-r--r-- | ripd/ChangeLog | 13 | ||||
-rw-r--r-- | ripd/rip_interface.c | 22 | ||||
-rw-r--r-- | ripd/ripd.c | 131 | ||||
-rw-r--r-- | ripd/ripd.h | 3 |
4 files changed, 129 insertions, 40 deletions
diff --git a/ripd/ChangeLog b/ripd/ChangeLog index d83714ae..fdfbdc16 100644 --- a/ripd/ChangeLog +++ b/ripd/ChangeLog @@ -1,3 +1,16 @@ +2004-01-23 sowmini.varadhan@sun.com + + * 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). + * rip_send_packet: don't send packets with source set to + ZEBRA_IFA_SECONDARY connected addresses; improved debug messages; + 2003-06-07 Andrew J. Schorr <aschorr@telemetry-investments.com> * Allow ripd to receive RIPv1 diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index e3aea186..1d6ce048 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -146,16 +146,20 @@ rip_interface_multicast_set (int sock, struct connected *connected, 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 @@ rip_interface_multicast_set (int sock, struct connected *connected, 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 @@ rip_interface_multicast_set (int sock, struct connected *connected, 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 @@ rip_request_interface_send (struct interface *ifp, u_char version) 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 @@ rip_request_interface_send (struct interface *ifp, u_char version) 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 @@ rip_request_neighbor (struct in_addr addr) 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. */ diff --git a/ripd/ripd.c b/ripd/ripd.c index 29e70c44..4b5a19a9 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -61,7 +61,7 @@ void rip_event (enum rip_event, int); 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,44 @@ rip_response_process (struct rip_packet *packet, int size, /* 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) + { + /* + * ZEBRA_IFA_SECONDARY is set on linux when an interface is configured + * with multiple addresses on the same subnet: the first address + * on the subnet is configured "primary", and all subsequent addresses + * on that subnet are treated as "secondary" addresses. + * In order to avoid routing-table bloat on other rip listeners, + * we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs. + * XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY + * flag is set, we would end up sending a packet for a "secondary" + * source address on non-linux systems. + */ + 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 +1285,7 @@ rip_send_packet (caddr_t buf, int size, struct sockaddr_in *to, { sin.sin_port = to->sin_port; sin.sin_addr = to->sin_addr; + send_sock = rip->sock; } else { @@ -1259,11 +1293,28 @@ rip_send_packet (caddr_t buf, int size, struct sockaddr_in *to, 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 +1324,9 @@ rip_send_packet (caddr_t buf, int size, struct sockaddr_in *to, if (ret < 0) zlog_warn ("can't send packet : %s", strerror (errno)); + if (!to) + close(send_sock); + return ret; } @@ -1456,7 +1510,7 @@ rip_request_process (struct rip_packet *packet, int size, /* All route with split horizon */ rip_output_process (ifp, NULL, from, rip_all_route, packet->version, - &saddr); + NULL, &saddr); } else { @@ -1488,7 +1542,7 @@ rip_request_process (struct rip_packet *packet, int size, } 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++; } @@ -1983,7 +2037,7 @@ rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p, 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; @@ -2243,7 +2297,7 @@ rip_output_process (struct interface *ifp, struct prefix *ifaddr, 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), @@ -2259,7 +2313,8 @@ rip_output_process (struct interface *ifp, struct prefix *ifaddr, 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), @@ -2275,12 +2330,13 @@ rip_output_process (struct interface *ifp, struct prefix *ifaddr, /* 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)) @@ -2289,7 +2345,7 @@ rip_update_interface (struct interface *ifp, u_char version, int route_type, zlog_info ("multicast announce on %s ", ifp->name); rip_output_process (ifp, NULL, NULL, route_type, rip->version_send, - saddr); + sconn, saddr); return; } @@ -2317,7 +2373,7 @@ rip_update_interface (struct interface *ifp, u_char version, int route_type, 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); } } } @@ -2371,10 +2427,11 @@ rip_update_process (int route_type) 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); @@ -2383,12 +2440,14 @@ rip_update_process (int route_type) 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; + } } } @@ -2413,7 +2472,8 @@ rip_update_process (int route_type) 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); } } @@ -2589,12 +2649,11 @@ rip_create () /* 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)); @@ -2603,6 +2662,20 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, 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) { @@ -2613,10 +2686,8 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, 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); diff --git a/ripd/ripd.h b/ripd/ripd.h index 29e4f67c..13a72a11 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -377,7 +377,8 @@ void rip_offset_init (); 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 *); |