Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package smcroute for openSUSE:Factory checked in at 2021-09-14 21:14:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/smcroute (Old) and /work/SRC/openSUSE:Factory/.smcroute.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "smcroute" Tue Sep 14 21:14:45 2021 rev:5 rq:918874 version:2.5.2 Changes: -------- --- /work/SRC/openSUSE:Factory/smcroute/smcroute.changes 2021-08-23 10:09:49.624130443 +0200 +++ /work/SRC/openSUSE:Factory/.smcroute.new.1899/smcroute.changes 2021-09-14 21:15:05.552446425 +0200 @@ -1,0 +2,24 @@ +Fri Aug 27 16:26:10 UTC 2021 - Martin Hauke <mar...@gmx.de> + +- Update to version 2.5.2 + Changes + * Allow installing routes with no outbound interfaces. + * Reinitialize VIFs on reload in case of new interfaces. + * Handle cases when interfaces change ifindex, i.e. they've first + been * removed and then re-added with the same name. + Fixes + * Fix VIF leak when deleting interfaces with MRDISC enabled. + * Fix handling when an (S,G) moves to another IIF. This fixes + issues where the SMCRoute kernel cache was out of sync with + the kernel MFC. + * Fix handling of lost/disabled interfaces at reload. This fixes + a couple of issues where routes were not updated properly at + runtime. + * Update interface flags on reload, this fixes issues when + SMCRoute failed to detect interfaces that had their MULTICAST + flag set or cleared at runtime. + * Skip setsockopt() for IPC sockets. This fixes warnings in + syslog about failing to disable MULTICAST_LOOP and + MULTICAST_ALL. + +------------------------------------------------------------------- Old: ---- smcroute-2.5.1.tar.gz New: ---- smcroute-2.5.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ smcroute.spec ++++++ --- /var/tmp/diff_new_pack.9mOkaY/_old 2021-09-14 21:15:06.068446932 +0200 +++ /var/tmp/diff_new_pack.9mOkaY/_new 2021-09-14 21:15:06.072446936 +0200 @@ -18,7 +18,7 @@ Name: smcroute -Version: 2.5.1 +Version: 2.5.2 Release: 0 Summary: Static multicast routing for UNIX License: GPL-3.0-only ++++++ smcroute-2.5.1.tar.gz -> smcroute-2.5.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/.github/workflows/release.yml new/smcroute-2.5.2/.github/workflows/release.yml --- old/smcroute-2.5.1/.github/workflows/release.yml 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/.github/workflows/release.yml 2021-08-27 17:55:39.000000000 +0200 @@ -50,7 +50,8 @@ ./configure --prefix= --enable-mrdisc --enable-test - name: Build release ... run: | - make release + sudo chmod a+rw /var/run/xtables.lock + make release || (cat test/test-suite.log; false) ls -lF ../ mkdir -p artifacts/ mv ../*.tar.* artifacts/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/ChangeLog.md new/smcroute-2.5.2/ChangeLog.md --- old/smcroute-2.5.1/ChangeLog.md 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/ChangeLog.md 2021-08-27 17:55:39.000000000 +0200 @@ -3,6 +3,28 @@ All notable changes to the project are documented in this file. +[v2.5.2][] - 2021-08-27 +----------------------- + +### Changes +- Allow installing routes with no outbound interfaces +- Reinitialize VIFs on reload in case of new interfaces +- Handle cases when interfaces change ifindex, i.e. they've first been + removed and then re-added with the same name + +### Fixes +- Fix VIF leak when deleting interfaces with MRDISC enabled +- Fix handling when an (S,G) moves to another IIF. This fixes issues + where the SMCRoute kernel cache was out of sync with the kernel MFC +- Fix handling of lost/disabled interfaces at reload. This fixes a + couple of issues where routes were not updated properly at runtime +- Update interface flags on reload, this fixes issues when SMCRoute + failed to detect interfaces that had their MULTICAST flag set or + cleared at runtime +. Skip `setsockopt()` for IPC sockets. This fixes warnings in syslog + about failing to disable `MULTICAST_LOOP` and `MULTICAST_ALL` + + [v2.5.1][] - 2021-08-22 ----------------------- @@ -552,8 +574,9 @@ [mrdisc]: https://github.com/troglobit/mrdisc [RFC4286]: https://tools.ietf.org/html/rfc4286 -[UNRELEASED]: https://github.com/troglobit/smcroute/compare/2.5.1...HEAD -[v2.5.1]: https://github.com/troglobit/smcroute/compare/2.5.1...2.5.1 +[UNRELEASED]: https://github.com/troglobit/smcroute/compare/2.5.2...HEAD +[v2.5.2]: https://github.com/troglobit/smcroute/compare/2.5.1...2.5.2 +[v2.5.1]: https://github.com/troglobit/smcroute/compare/2.5.0...2.5.1 [v2.5.0]: https://github.com/troglobit/smcroute/compare/2.4.4...2.5.0 [v2.4.4]: https://github.com/troglobit/smcroute/compare/2.4.3...2.4.4 [v2.4.3]: https://github.com/troglobit/smcroute/compare/2.4.2...2.4.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/README.md new/smcroute-2.5.2/README.md --- old/smcroute-2.5.1/README.md 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/README.md 2021-08-27 17:55:39.000000000 +0200 @@ -227,8 +227,8 @@ smcroutectl help -**Note:** Root privileges are required by default for `smcroutectl` due - to the IPC socket permissions. +> **Note:** Root privileges are required by default for `smcroutectl` due +> to the IPC socket permissions. Wildcard Routes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/configure.ac new/smcroute-2.5.2/configure.ac --- old/smcroute-2.5.1/configure.ac 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/configure.ac 2021-08-27 17:55:39.000000000 +0200 @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(SMCRoute, 2.5.1, https://github.com/troglobit/smcroute/issues, smcroute, https://troglobit.com/smcroute.html) +AC_INIT(SMCRoute, 2.5.2, https://github.com/troglobit/smcroute/issues, smcroute, https://troglobit.com/smcroute.html) AC_CONFIG_AUX_DIR(aux) AM_INIT_AUTOMAKE([1.11 foreign]) AM_SILENT_RULES([yes]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/conf.c new/smcroute-2.5.2/src/conf.c --- old/smcroute-2.5.1/src/conf.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/conf.c 2021-08-27 17:55:39.000000000 +0200 @@ -216,13 +216,12 @@ DEBUG("mroute: checking for input iface %s ...", iif); while (iface_match_vif_by_name(iif, &state_in, &iface_in) != NO_VIF) { char src[INET_ADDRSTR_LEN], grp[INET_ADDRSTR_LEN]; - int i, total = 0; vif = iface_get_vif(family, iface_in); DEBUG("mroute: input iface %s has vif %d", iif, vif); mroute.inbound = vif; - for (i = 0; i < num; i++) { + for (int i = 0; i < num; i++) { iface_match_init(&state_out); DEBUG("mroute: checking for %s ...", oif[i]); @@ -237,7 +236,6 @@ /* Use configured TTL threshold for the output phyint */ mroute.ttl[vif] = iface->threshold; - total++; } if (!state_out.match_count) WARN("mroute: outbound %s is not a known phyint, skipping", oif[i]); @@ -247,15 +245,10 @@ continue; if (cmd) { - if (!total) { - WARN("mroute: no outbound interfaces, cannot add multicast route."); - rc += 1; - } else { - smclog(LOG_DEBUG, "mroute: adding route from %s (%s/%u,%s/%u)", iface_in->ifname, - inet_addr2str(&mroute.source, src, sizeof(src)), mroute.src_len, - inet_addr2str(&mroute.group, grp, sizeof(grp)), mroute.len); - rc += mroute_add_route(&mroute); - } + smclog(LOG_DEBUG, "mroute: adding route from %s (%s/%u,%s/%u)", iface_in->ifname, + inet_addr2str(&mroute.source, src, sizeof(src)), mroute.src_len, + inet_addr2str(&mroute.group, grp, sizeof(grp)), mroute.len); + rc += mroute_add_route(&mroute); } else { smclog(LOG_DEBUG, "mroute: deleting route from %s (%s/%u,%s/%u)", iface_in->ifname, inet_addr2str(&mroute.source, src, sizeof(src)), mroute.src_len, @@ -282,7 +275,7 @@ struct ifmatch ifm; iface_match_init(&ifm); - iface = iface_match_by_name(iif, &ifm); + iface = iface_match_by_name(iif, 1, &ifm); if (!iface) return 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/iface.c new/smcroute-2.5.2/src/iface.c --- old/smcroute-2.5.1/src/iface.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/iface.c 2021-08-27 17:55:39.000000000 +0200 @@ -41,12 +41,12 @@ #include "util.h" static TAILQ_HEAD(iflist, iface) iface_list = TAILQ_HEAD_INITIALIZER(iface_list); +extern int do_vifs; /** * iface_update - Check of new interfaces - * @do_vifs: If set, then mark all interfaces found as "in use" */ -void iface_update(int do_vifs) +void iface_update(void) { struct ifaddrs *ifaddr, *ifa; @@ -57,13 +57,25 @@ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { struct iface *iface; + int ifindex; + + ifindex = if_nametoindex(ifa->ifa_name); /* Check if already added? */ iface = iface_find_by_name(ifa->ifa_name); if (iface) { smclog(LOG_DEBUG, "Found %s, updating ...", ifa->ifa_name); + iface->flags = ifa->ifa_flags; + + if (ifindex != iface->ifindex || (iface->flags & IFF_MULTICAST) != IFF_MULTICAST) { + mcgroup_prune(ifa->ifa_name); + mroute_del_vif(ifa->ifa_name); + } + + iface->ifindex = ifindex; if (!iface->inaddr.s_addr && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) iface->inaddr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + if (do_vifs) iface->unused = 0; @@ -102,14 +114,13 @@ /** * iface_init - Probe for interaces at startup - * @do_vifs: If set, then mark all interfaces found as "in use" * * Builds up a vector with active system interfaces. Must be called * before any other interface functions in this module! */ -void iface_init(int do_vifs) +void iface_init(void) { - iface_update(do_vifs); + iface_update(); } /** @@ -260,6 +271,7 @@ /** * iface_match_by_name - Find matching interfaces by name pattern * @ifname: Interface name pattern + * @reload: Set while reloading .conf * @state: Iterator state * * Interface name patterns use iptables- syntax, i.e. perform prefix @@ -269,7 +281,7 @@ * Pointer to a @struct iface of the next matching interface, or %NULL if no * (more) interfaces exist (or are up). */ -struct iface *iface_match_by_name(const char *ifname, struct ifmatch *state) +struct iface *iface_match_by_name(const char *ifname, int reload, struct ifmatch *state) { unsigned int match_len = UINT_MAX; @@ -283,10 +295,12 @@ struct iface *iface = state->iface; if (!strncmp(ifname, iface->ifname, match_len)) { - state->iface = TAILQ_NEXT(iface, link); - state->match_count++; + if (reload || !iface->unused) { + state->iface = TAILQ_NEXT(iface, link); + state->match_count++; - return iface; + return iface; + } } state->iface = TAILQ_NEXT(iface, link); @@ -365,7 +379,7 @@ { struct iface *iface; - while ((iface = iface_match_by_name(ifname, state))) { + while ((iface = iface_match_by_name(ifname, 0, state))) { if (iface->vif != NO_VIF) { if (found) *found = iface; @@ -394,7 +408,7 @@ { struct iface *iface; - while ((iface = iface_match_by_name(ifname, state))) { + while ((iface = iface_match_by_name(ifname, 0, state))) { if (iface->mif != NO_VIF) { if (found) *found = iface; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/iface.h new/smcroute-2.5.2/src/iface.h --- old/smcroute-2.5.1/src/iface.h 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/iface.h 2021-08-27 17:55:39.000000000 +0200 @@ -36,9 +36,9 @@ size_t match_count; }; -void iface_init (int do_vifs); +void iface_init (void); void iface_exit (void); -void iface_update (int do_vifs); +void iface_update (void); struct iface *iface_iterator (int first); struct iface *iface_outbound_iterator (struct mroute *route, int first); @@ -48,7 +48,7 @@ struct iface *iface_find_by_inbound (struct mroute *route); void iface_match_init (struct ifmatch *state); -struct iface *iface_match_by_name (const char *ifname, struct ifmatch *state); +struct iface *iface_match_by_name (const char *ifname, int reload, struct ifmatch *state); int ifname_is_wildcard (const char *ifname); vifi_t iface_get_vif (int af_family, struct iface *iface); @@ -57,12 +57,15 @@ int iface_show (int sd, int detail); +/* + * Check if interface exists, at all, on the system + */ static inline int iface_exist(char *ifname) { struct ifmatch ifm; iface_match_init(&ifm); - return iface_match_by_name(ifname, &ifm) != NULL; + return iface_match_by_name(ifname, 1, &ifm) != NULL; } static inline int iface_ifname_maxlen(void) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/inet.h new/smcroute-2.5.2/src/inet.h --- old/smcroute-2.5.1/src/inet.h 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/inet.h 2021-08-27 17:55:39.000000000 +0200 @@ -75,4 +75,14 @@ int inet_iter_init (struct inet_iter *iter, inet_addr_t *addr, int len); int inet_iterator (struct inet_iter *iter, inet_addr_t *addr); + +static inline int inet_max_len (inet_addr_t *addr) +{ +#ifdef HAVE_IPV6_MULTICAST_HOST + if (addr->ss_family == AF_INET6) + return 128; +#endif + return 32; +} + #endif /* SMCROUTE_INET_H_ */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/mcgroup.c new/smcroute-2.5.2/src/mcgroup.c --- old/smcroute-2.5.1/src/mcgroup.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/mcgroup.c 2021-08-27 17:55:39.000000000 +0200 @@ -140,7 +140,7 @@ static struct iface *match_valid_iface(const char *ifname, struct ifmatch *state) { - struct iface *iface = iface_match_by_name(ifname, state); + struct iface *iface = iface_match_by_name(ifname, 0, state); if (!iface && !state->match_count) smclog(LOG_DEBUG, "unknown interface %s", ifname); @@ -398,6 +398,16 @@ void mcgroup_reload_end(void) { struct mcgroup *entry, *tmp; + struct iface *iface; + int first = 1; + + while ((iface = iface_iterator(first))) { + char dummy[IFNAMSIZ]; + + first = 0; + if (iface->unused || !if_indextoname(iface->ifindex, dummy)) + mcgroup_prune(iface->ifname); + } TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { if (!entry->unused) @@ -407,62 +417,84 @@ } } +/* + * When an interface is removed from the system, or its flags are + * changed to exclude the MULTICAST flag, we must prune groups. + */ +void mcgroup_prune(char *ifname) +{ + struct mcgroup *entry, *tmp; + + TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { + if (strcmp(entry->ifname, ifname)) + continue; + + mcgroup_action(0, entry->ifname, &entry->source, entry->src_len, &entry->group, entry->len); + } +} + +static int show_mcgroup(int sd, struct mcgroup *entry) +{ + int max_len = inet_max_len(&entry->group); + char sg[INET_ADDRSTR_LEN * 2 + 10 + 3]; + char src[INET_ADDRSTR_LEN] = "*"; + char grp[INET_ADDRSTR_LEN]; + char line[256]; + + if (!is_anyaddr(&entry->source)) + inet_addr2str(&entry->source, src, sizeof(src)); + inet_addr2str(&entry->group, grp, sizeof(grp)); + + snprintf(sg, sizeof(sg), "(%s", src); + if (entry->src_len != max_len) + snprintf(line, sizeof(line), "/%u, ", entry->src_len); + else + snprintf(line, sizeof(line), ", "); + strlcat(sg, line, sizeof(sg)); + + if (entry->len != max_len) + snprintf(line, sizeof(line), "%s/%u)", grp, entry->len); + else + snprintf(line, sizeof(line), "%s)", grp); + strlcat(sg, line, sizeof(sg)); + + snprintf(line, sizeof(line), "%-42s %s\n", sg, entry->ifname); + if (ipc_send(sd, line, strlen(line)) < 0) { + smclog(LOG_ERR, "Failed sending reply to client: %s", strerror(errno)); + return -1; + } + + return 0; +} + /* Write all joined IGMP/MLD groups to client socket */ int mcgroup_show(int sd, int detail) { - char sg[INET_ADDRSTR_LEN * 2 + 10 + 3]; + char *conf_str = "Group Memberships Table_\n"; + char *kern_str = "Kernel Group Membership Table_\n"; struct mcgroup *entry; char line[256]; - + if (TAILQ_EMPTY(&conf_list)) return 0; + ipc_send(sd, conf_str, strlen(conf_str)); snprintf(line, sizeof(line), "%-42s %-16s=\n", "GROUP (S,G)", "IIF"); ipc_send(sd, line, strlen(line)); TAILQ_FOREACH(entry, &conf_list, link) { - char src[INET_ADDRSTR_LEN] = "*"; - char grp[INET_ADDRSTR_LEN]; - struct iface *iface; - int max_len; - - iface = iface_find_by_name(entry->ifname); - if (!iface) - continue; - -#ifdef HAVE_IPV6_MULTICAST_HOST - if (entry->group.ss_family == AF_INET6) - max_len = 128; - else -#endif - max_len = 32; - - if (!is_anyaddr(&entry->source)) - inet_addr2str(&entry->source, src, sizeof(src)); - inet_addr2str(&entry->group, grp, sizeof(grp)); - - snprintf(sg, sizeof(sg), "(%s", src); - if (entry->src_len != max_len) - snprintf(line, sizeof(line), "/%u, ", entry->src_len); - else - snprintf(line, sizeof(line), ", "); - strlcat(sg, line, sizeof(sg)); - - if (entry->len != max_len) - snprintf(line, sizeof(line), "%s/%u)", grp, entry->len); - else - snprintf(line, sizeof(line), "%s)", grp); - strlcat(sg, line, sizeof(sg)); - - snprintf(line, sizeof(line), "%-42s %s\n", sg, iface->ifname); - if (ipc_send(sd, line, strlen(line)) < 0) { - smclog(LOG_ERR, "Failed sending reply to client: %s", strerror(errno)); - return -1; - } + if (show_mcgroup(sd, entry) < 0) + return 1; } - if (detail) { - /* XXX: Show all from kern_list as well */ + if (!detail) + return 0; + + ipc_send(sd, kern_str, strlen(kern_str)); + snprintf(line, sizeof(line), "%-42s %-16s=\n", "GROUP (S,G)", "IIF"); + TAILQ_FOREACH(entry, &kern_list, link) { + if (show_mcgroup(sd, entry) < 0) + return 1; } return 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/mcgroup.h new/smcroute-2.5.2/src/mcgroup.h --- old/smcroute-2.5.1/src/mcgroup.h 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/mcgroup.h 2021-08-27 17:55:39.000000000 +0200 @@ -20,6 +20,7 @@ void mcgroup_reload_beg(void); void mcgroup_reload_end(void); +void mcgroup_prune (char *ifname); void mcgroup_init (void); void mcgroup_exit (void); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/mroute.c new/smcroute-2.5.2/src/mroute.c --- old/smcroute-2.5.1/src/mroute.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/mroute.c 2021-08-27 17:55:39.000000000 +0200 @@ -59,6 +59,8 @@ static int mroute_dyn_add (struct mroute *route); static int is_match (struct mroute *rule, struct mroute *cand); static int is_exact_match (struct mroute *rule, struct mroute *cand); +static int mfc_install (struct mroute *route); +static int mfc_uninstall (struct mroute *route); /* Check for kernel IGMPMSG_NOCACHE for (*,G) hits. I.e., source-less routes. */ static void handle_nocache4(int sd, void *arg) @@ -218,7 +220,6 @@ if (kern_mroute_exit()) return; - /* Free list of (*,G) routes on SIGHUP */ TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { TAILQ_REMOVE(&conf_list, entry, link); free(entry); @@ -229,6 +230,29 @@ } } +/* + * Prune VIF from all existing routes and update kernel MFC. If VIF is + * used as inbound, prune entire route, otherwise just the outbound. + */ +static void mroute4_prune_vif(int vif) +{ + struct mroute *entry, *tmp; + + TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { + if (entry->group.ss_family != AF_INET) + continue; + + if (entry->inbound == vif) { + TAILQ_REMOVE(&conf_list, entry, link); + entry->unused = 1; + mfc_uninstall(entry); + free(entry); + } else if (entry->ttl[vif] > 0) { + entry->ttl[vif] = 0; + mfc_install(entry); + } + } +} /* Create a virtual interface from @iface so it can be used for IPv4 multicast routing. */ static int mroute4_add_vif(struct iface *iface) @@ -268,16 +292,28 @@ static int mroute4_del_vif(struct iface *iface) { + int rc = 0; + if (iface->mrdisc) - return mrdisc_deregister(iface->vif); + mrdisc_deregister(iface->vif); - if (kern_vif_del(iface) && errno != ENOENT) { - smclog(LOG_ERR, "Failed deleting VIF for iface %s: %s", iface->ifname, strerror(errno)); - return -1; + if (kern_vif_del(iface)) { + switch (errno) { + case ENOENT: + case EADDRNOTAVAIL: + break; + default: + smclog(LOG_ERR, "Failed deleting VIF for iface %s: %s", iface->ifname, strerror(errno)); + break; + } + rc = -1; } + + if (iface->vif >= 0 && iface->vif < ALL_VIFS) + mroute4_prune_vif(iface->vif); iface->vif = -1; - return 0; + return rc; } static int is_exact_match(struct mroute *rule, struct mroute *cand) @@ -334,14 +370,8 @@ static int is_ssm(struct mroute *route) { - int max_len; + int max_len = inet_max_len(&route->group); -#ifdef HAVE_IPV6_MULTICAST_HOST - if (route->group.ss_family == AF_INET6) - max_len = 128; - else -#endif - max_len = 32; return !is_anyaddr(&route->source) && route->src_len == max_len && route->len == max_len; } @@ -490,20 +520,15 @@ } TAILQ_FOREACH(kern, &kern_list, link) { - int diff = 0; - if (!is_match(route, kern)) continue; for (size_t i = 0; i < NELEMS(route->ttl); i++) { - if (route->ttl[i] > 0 && kern->ttl[i] != route->ttl[i]) { + if (route->ttl[i] > 0 && kern->ttl[i] != route->ttl[i]) kern->ttl[i] = route->ttl[i]; - diff++; - } } - if (diff) - kern_mroute_add(kern); + kern_mroute_add(kern); } return 0; @@ -812,6 +837,30 @@ } #ifdef HAVE_IPV6_MULTICAST_ROUTING +/* + * Prune VIF from all existing routes and update kernel MFC. If VIF is + * used as inbound, prune entire route, otherwise just the outbound. + */ +static void mroute6_prune_mif(int mif) +{ + struct mroute *entry, *tmp; + + TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { + if (entry->group.ss_family != AF_INET6) + continue; + + if (entry->inbound == mif) { + TAILQ_REMOVE(&conf_list, entry, link); + entry->unused = 1; + mfc_uninstall(entry); + free(entry); + } else if (entry->ttl[mif] > 0) { + entry->ttl[mif] = 0; + mfc_install(entry); + } + } +} + /* Create a virtual interface from @iface so it can be used for IPv6 multicast routing. */ static int mroute6_add_mif(struct iface *iface) { @@ -848,13 +897,25 @@ static int mroute6_del_mif(struct iface *iface) { + int rc = 0; + if (kern_mif_del(iface) && errno != ENOENT) { - smclog(LOG_ERR, "Failed deleting MIF for iface %s: %s", iface->ifname, strerror(errno)); - return -1; + switch (errno) { + case ENOENT: + case EADDRNOTAVAIL: + break; + default: + smclog(LOG_ERR, "Failed deleting MIF for iface %s: %s", iface->ifname, strerror(errno)); + break; + } + rc = -1; } + + if (iface->mif >= 0 && iface->mif < ALL_VIFS) + mroute6_prune_mif(iface->mif); iface->mif = -1; - return 0; + return rc; } #endif /* HAVE_IPV6_MULTICAST_ROUTING */ @@ -932,11 +993,11 @@ int rc = 0; iface_match_init(&state); - while ((iface = iface_match_by_name(ifname, &state))) { + while ((iface = iface_match_by_name(ifname, 1, &state))) { smclog(LOG_DEBUG, "Creating/updating multicast VIF for %s TTL %d", iface->ifname, ttl); - iface->unused = 0; iface->mrdisc = mrdisc; iface->threshold = ttl; + iface->unused = 0; rc += mroute4_add_vif(iface); #ifdef HAVE_IPV6_MULTICAST_ROUTING rc += mroute6_add_mif(iface); @@ -959,7 +1020,7 @@ int rc = 0; iface_match_init(&state); - while ((iface = iface_match_by_name(ifname, &state))) { + while ((iface = iface_match_by_name(ifname, 1, &state))) { smclog(LOG_DEBUG, "Removing multicast VIFs for %s", iface->ifname); rc += mroute4_del_vif(iface); #ifdef HAVE_IPV6_MULTICAST_ROUTING @@ -995,24 +1056,30 @@ } } -void mroute_reload_end(void) +void mroute_reload_end(int do_vifs) { struct mroute *entry, *tmp; struct iface *iface; int first = 1; + while ((iface = iface_iterator(first))) { + char dummy[IFNAMSIZ]; + + first = 0; + if (iface->unused || !if_indextoname(iface->ifindex, dummy)) { + mroute_del_vif(iface->ifname); + } else if (do_vifs) + mroute_add_vif(iface->ifname, iface->mrdisc, iface->threshold); + } + TAILQ_FOREACH_SAFE(entry, &conf_list, link, tmp) { if (entry->unused) mroute_del_route(entry); } - while ((iface = iface_iterator(first))) { - first = 0; - if (iface->unused) { - mroute_del_vif(iface->ifname); - iface->unused = 0; - } - } + /* retry add if .conf changed IIF for routes, not until del (above) can we add */ + TAILQ_FOREACH(entry, &conf_list, link) + mfc_install(entry); } static int show_mroute(int sd, struct mroute *r, int inw, int detail) @@ -1024,12 +1091,9 @@ char sg[(INET_ADDRSTRLEN + 3) * 2 + 5]; char buf[MAX_MC_VIFS * 17 + 80]; struct iface *iface; - int max_len = 32; + int max_len; -#ifdef HAVE_IPV6_MULTICAST_ROUTING - if (r->group.ss_family == AF_INET6) - max_len = 128; -#endif + max_len = inet_max_len(&r->group); if (!is_anyaddr(&r->source)) { inet_addr2str(&r->source, src, sizeof(src)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/mroute.h new/smcroute-2.5.2/src/mroute.h --- old/smcroute-2.5.1/src/mroute.h 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/mroute.h 2021-08-27 17:55:39.000000000 +0200 @@ -116,7 +116,7 @@ int mroute_del_route (struct mroute *mroute); void mroute_reload_beg (void); -void mroute_reload_end (void); +void mroute_reload_end (int do_vifs); int mroute_show (int sd, int detail); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/smcrouted.c new/smcroute-2.5.2/src/smcrouted.c --- old/smcroute-2.5.1/src/smcrouted.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/smcrouted.c 2021-08-27 17:55:39.000000000 +0200 @@ -89,11 +89,11 @@ mcgroup_reload_beg(); mroute_reload_beg(); - iface_update(do_vifs); + iface_update(); conf_read(conf_file, do_vifs); + mroute_reload_end(do_vifs); mcgroup_reload_end(); - mroute_reload_end(); /* Acknowledge client SIGHUP/reload */ notify_ready(NULL, uid, gid); @@ -193,7 +193,7 @@ /* * Build list of multicast-capable physical interfaces */ - iface_init(do_vifs); + iface_init(); if (mroute_init(do_vifs, table_id, cache_tmo)) { if (errno == EADDRINUSE) @@ -470,7 +470,7 @@ if (conf_vrfy) { smclog(LOG_INFO, "Verifying configuration file %s ...", conf_file); - iface_init(do_vifs); + iface_init(); c = conf_read(conf_file, do_vifs); iface_exit(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/src/socket.c new/smcroute-2.5.2/src/socket.c --- old/smcroute-2.5.1/src/socket.c 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/src/socket.c 2021-08-27 17:55:39.000000000 +0200 @@ -98,29 +98,29 @@ if (sd < 0) return -1; + if (domain == AF_UNIX) + goto done; + #ifdef HAVE_IPV6_MULTICAST_HOST - if (domain == AF_INET6) { - if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val))) - smclog(LOG_WARNING, "failed disabling IPV6_MULTICAST_LOOP: %s", - strerror(errno)); + if (domain == AF_INET6) { + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val))) + smclog(LOG_WARNING, "failed disabling IPV6_MULTICAST_LOOP: %s", strerror(errno)); #ifdef IPV6_MULTICAST_ALL - if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_ALL, &val, sizeof(val))) - smclog(LOG_WARNING, "failed disabling IPV6_MULTICAST_ALL: %s", - strerror(errno)); + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_ALL, &val, sizeof(val))) + smclog(LOG_WARNING, "failed disabling IPV6_MULTICAST_ALL: %s", strerror(errno)); #endif - } else + } else #endif /* HAVE_IPV6_MULTICAST_HOST */ - { - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val))) - smclog(LOG_WARNING, "failed disabling IP_MULTICAST_LOOP: %s", - strerror(errno)); + { + + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val))) + smclog(LOG_WARNING, "failed disabling IP_MULTICAST_LOOP: %s", strerror(errno)); #ifdef IP_MULTICAST_ALL - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_ALL, &val, sizeof(val))) - smclog(LOG_WARNING, "failed disabling IP_MULTICAST_ALL: %s", - strerror(errno)); + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_ALL, &val, sizeof(val))) + smclog(LOG_WARNING, "failed disabling IP_MULTICAST_ALL: %s", strerror(errno)); #endif - } - + } +done: if (socket_register(sd, cb, arg) < 0) { close(sd); return -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/test/Makefile.am new/smcroute-2.5.2/test/Makefile.am --- old/smcroute-2.5.1/test/Makefile.am 2021-08-22 09:36:54.000000000 +0200 +++ new/smcroute-2.5.2/test/Makefile.am 2021-08-27 17:55:39.000000000 +0200 @@ -1,5 +1,5 @@ EXTRA_DIST = adv.sh basic.sh bridge.sh dyn.sh expire.sh gre.sh ipv6.sh include.sh -EXTRA_DIST += isolated.sh join.sh joinlen.sh lib.sh multi.sh mem.sh mrdisc.sh +EXTRA_DIST += isolated.sh join.sh joinlen.sh lib.sh lost.sh multi.sh mem.sh mrdisc.sh EXTRA_DIST += poison.sh reload.sh reload6.sh vlan.sh vrfy.sh CLEANFILES = *~ *.trs *.log TEST_EXTENSIONS = .sh @@ -16,6 +16,7 @@ TESTS += isolated.sh TESTS += join.sh TESTS += joinlen.sh +TESTS += lost.sh TESTS += mem.sh TESTS += mrdisc.sh TESTS += multi.sh diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/smcroute-2.5.1/test/lost.sh new/smcroute-2.5.2/test/lost.sh --- old/smcroute-2.5.1/test/lost.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/smcroute-2.5.2/test/lost.sh 2021-08-27 17:55:39.000000000 +0200 @@ -0,0 +1,98 @@ +#!/bin/sh +# Verify handling when IIF for an (S,G) is changed or lost + +#set -x + +check_output() +{ + echo "ip mroute:" + ip mroute + ip mroute |tee "/tmp/$NM/result" | grep -q "$2" + oif=$? + grep -q "$1" "/tmp/$NM/result" + iif=$? + # shellcheck disable=SC2166 + [ "$oif" = "0" -a "$iif" = "0" ] || FAIL +} + +# shellcheck source=/dev/null +. "$(dirname "$0")/lib.sh" + +print "Creating world ..." +topo basic +ip addr add 10.0.0.1/24 dev a1 +ip addr add 20.0.0.1/24 dev a2 +ip -br a + +print "Creating config #1 ..." +cat <<EOF > "/tmp/$NM/conf" +phyint a1 enable +phyint a2 enable + +mgroup from a1 group 225.1.2.3 +mroute from a1 group 225.1.2.3 to a2 + +mgroup from a2 group 225.1.2.4 +mroute from a2 source 1.2.3.4 group 225.1.2.4 to a1 + +mgroup from a1 group 225.2.2.5 +mroute from a1 group 225.2.2.5 to a1 + +mgroup from a2 group 225.1.2.6 +mroute from a2 source 1.2.3.5 group 225.1.2.6 to a1 +EOF +cat "/tmp/$NM/conf" + +print "Starting smcrouted ..." +../src/smcrouted -f "/tmp/$NM/conf" -N -n -P "/tmp/$NM/pid" -l debug -u "/tmp/$NM/sock" & +sleep 1 + +cat /proc/net/ip_mr_vif +cat /proc/net/ip_mr_cache +../src/smcroutectl -pu "/tmp/$NM/sock" show groups +show_mroute + +check_output "(1.2.3.4,225.1.2.4) Iif: a2 Oifs: a1" + +print "Creating config #2 ..." +cat <<EOF > "/tmp/$NM/conf" +phyint a1 enable +phyint a2 enable + +mgroup from a1 group 225.1.2.3 +mroute from a1 group 225.1.2.3 to a2 + +mgroup from a1 group 225.1.2.4 +mroute from a1 source 1.2.3.4 group 225.1.2.4 to a2 + +mgroup from a2 group 225.2.2.5 +mroute from a2 group 225.2.2.5 to a1 + +mgroup from a2 group 225.1.2.6 +mroute from a2 source 1.2.3.5 group 225.1.2.6 to a1 +EOF +cat "/tmp/$NM/conf" +../src/smcroutectl -u "/tmp/$NM/sock" reload +sleep 1 + +check_output "(1.2.3.4,225.1.2.4) Iif: a1 Oifs: a2" + +print "Deleting and restoring interface a1 => new ifindex ..." +ip link del a1 +ip link add a1 type dummy +ip link set a1 up +ip link set a1 multicast on +ip addr add 10.0.0.1/24 dev a1 + +../src/smcroutectl -u "/tmp/$NM/sock" reload +sleep 1 + +check_output "(1.2.3.4,225.1.2.4) Iif: a1 Oifs: a2" + +print "Deleting interface a1 ..." +ip link del a1 +../src/smcroutectl -u "/tmp/$NM/sock" reload +sleep 1 +check_output "(1.2.3.5,225.1.2.6) Iif: a2 State: resolved" + +OK