X ipv6: route: Don't turn "multicast to loopback" routes into rejects
With multicast routing, if one wants to forward multicast traffic from external interfaces to lo, there must be a "daddr=group oif=lo" regular route in the system, because both IPv4 and IPv4 multicast-routing code complete forwarding of multicast packets by sending it to this regular route: IPv4: (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/ipmr.c?id=v5.18-rc5-28-ga7391ad35724#n1844) ipmr_queue_xmit rt = ip_route_output_ports(daddr, oif=vif->link) ... IPv6: (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/ip6mr.c?id=v5.18-rc5-28-ga7391ad35724#n2046) ip6mr_forward2 fl6 = (struct flowi6) { .flowi6_oif = vif->link, .daddr = ipv6h->daddr, }; dst = ip6_route_output(net, NULL, &fl6); For IPv4 the system does not reject setting up such a route, for example the following works ok: ip route add multicast 224.0.0.0/4 oif lo dev lo scope global and, even if such route is not explicitly setup, IPv4 continues to deliver packets to destination device due to the following ad-hoc tweak inside ip_route_output_key_hash_rcu: (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/route.c?id=v5.18-rc5-28-ga7391ad35724#n2747) err = fib_lookup(net, fl4, res, 0); if (err) { res->fi = NULL; res->table = NULL; if (fl4->flowi4_oif && (ipv4_is_multicast(fl4->daddr) || !fl4->flowi4_l3mdev)) { /* Apparently, routing tables are wrong. Assume, * that the destination is on link. * * WHY? DW. * Because we are allowed to send to iface * even if it has NO routes and NO assigned * addresses. When oif is specified, routing * tables are looked up with only one purpose: * to catch if destination is gatewayed, rather than * direct. Moreover, if MSG_DONTROUTE is set, * we send packet, ignoring both routing tables * and ifaddr state. --ANK * * * We could make it even if oif is unknown, * likely IPv6, but we do not. */ if (fl4->saddr == 0) fl4->saddr = inet_select_addr(dev_out, 0, RT_SCOPE_LINK); res->type = RTN_UNICAST; goto make_route; However for IPv6, even if e.g. the following seemingly succeeds: ip route add multicast ff1e::/16 oif lo dev lo scope global the systems automatically turns it into reject: (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/route.c?id=v5.18-rc5-28-ga7391ad35724#n3805) ip6_route_info_create /* We cannot add true routes via loopback here, they would * result in kernel looping; promote them to reject routes */ addr_type = ipv6_addr_type(&cfg->fc_dst); if (fib6_is_reject(cfg->fc_flags, rt->fib6_nh->fib_nh_dev, addr_type)) rt->fib6_flags = RTF_REJECT | RTF_NONEXTHOP; which makes the route essentially disabled because dst, that ip6_route_output will lookup, will come with dst->dev=lo (ok), but dst->output=ip6_pkt_discard_out which will not send the packet to anywhere. Turning lo-routes into rejects was first added in Linux 2.1.90pre1 (1998) as (9d11a517 in historic repository) ip6_route_add() ... + /* We cannot add true routes via loopback here, + they would result in kernel looping; promote them to reject routes + */ + if ((rtmsg->rtmsg_flags&RTF_REJECT) || + (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { + dev = dev_get("lo"); + rt->u.dst.output = ip6_pkt_discard; + rt->u.dst.input = ip6_pkt_discard; + rt->u.dst.error = -EHOSTUNREACH; + rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; + rt->rt6i_metric = rtmsg->rtmsg_metric; + rt->rt6i_dev = dev; + goto install_route; + } XXX however with multicast there should not be cycles because when multicast skb is forwarded, it is marked with IP6SKB_FORWARDED, and on next turn not processed on input if seen with this flag (see 7bc570c8 "[IPV6] MROUTE: Support multicast forwarding.", git.kernel.org/linus/7bc570c8) -> Allow establishing routes for multicast to loopback. XXX verify this in detail + add test that if we enable such forwarding we cannot put the system into infinite loop.
Showing
-
Maintainer
.
-
mentioned in commit nexedi/multicast-study@3e684fcf
Please register or sign in to comment