Commit 25bcc760 authored by Kirill Smelkov's avatar Kirill Smelkov

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.
parent 43eadf90
...@@ -3500,7 +3500,7 @@ static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type) ...@@ -3500,7 +3500,7 @@ static bool fib6_is_reject(u32 flags, struct net_device *dev, int addr_type)
{ {
if ((flags & RTF_REJECT) || if ((flags & RTF_REJECT) ||
(dev && (dev->flags & IFF_LOOPBACK) && (dev && (dev->flags & IFF_LOOPBACK) &&
!(addr_type & IPV6_ADDR_LOOPBACK) && !(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_MULTICAST)) &&
!(flags & (RTF_ANYCAST | RTF_LOCAL)))) !(flags & (RTF_ANYCAST | RTF_LOCAL))))
return true; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment