From 93e78e1d65976643ea8de54cfbe46f5741f49a9d Mon Sep 17 00:00:00 2001
From: David Lamparter <equinox@diac24.net>
Date: Sat, 13 Feb 2010 19:19:22 +0100
Subject: [PATCH] ipmr: new struct vifctln to support ifindex for MIF_ADD_VIF
it isn't currently possible to do multicast routing if multiple
interfaces with the same IP address are present in a system, as only the
first of them can be selected for mroute VIF creation.
fix this by adding struct vifctln (analogous to struct mreqn) which has
a new int vifi_ifindex field. the setsockopt call differentiates vifctl
from vifctln through the length field; non-present or zero vifi_ifindex
keeps the old behaviour, nonzero vifi_ifindex causes vifi_lcladdr to be
ignored in favour of vifi_ifindex.
Signed-off-by: David Lamparter <equinox@diac24.net>
---
include/linux/mroute.h | 14 ++++++++++++++
net/ipv4/ipmr.c | 19 +++++++++++++------
2 files changed, 27 insertions(+), 6 deletions(-)
@@ -63,6 +63,20 @@ struct vifctl {
struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */
};
+/* Linux extension: reference VIF by ifindex.
+ * this struct MUST exactly retain the fields from vifctl for compatibility!
+ * vifc_ifindex is used if nonzero, otherwise fall back to old behaviour
+ */
+struct vifctln {
+ vifi_t vifc_vifi; /* Index of VIF */
+ unsigned char vifc_flags; /* VIFF_ flags */
+ unsigned char vifc_threshold; /* ttl limit */
+ unsigned int vifc_rate_limit; /* Rate limiter values (NI) */
+ struct in_addr vifc_lcl_addr; /* Our address */
+ struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */
+ int vifc_ifindex; /* Index of real Interface */
+};
+
#define VIFF_TUNNEL 0x1 /* IPIP tunnel */
#define VIFF_SRCRT 0x2 /* NI */
#define VIFF_REGISTER 0x4 /* register vif */
@@ -23,6 +23,7 @@
* Carlos Picoto : PIMv1 Support
* Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
* Relax this requrement to work with older peers.
+ * David Lamparter : struct vifctln introduced
*
*/
@@ -135,7 +136,7 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
}
static
-struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
+struct net_device *ipmr_new_tunnel(struct net *net, struct vifctln *v)
{
struct net_device *dev;
@@ -149,6 +150,7 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
struct in_device *in_dev;
memset(&p, 0, sizeof(p));
+ p.link = v->vifc_ifindex;
p.iph.daddr = v->vifc_rmt_addr.s_addr;
p.iph.saddr = v->vifc_lcl_addr.s_addr;
p.iph.version = 4;
@@ -426,7 +428,7 @@ static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls)
}
}
-static int vif_add(struct net *net, struct vifctl *vifc, int mrtsock)
+static int vif_add(struct net *net, struct vifctln *vifc, int mrtsock)
{
int vifi = vifc->vifc_vifi;
struct vif_device *v = &net->ipv4.vif_table[vifi];
@@ -470,7 +472,10 @@ static int vif_add(struct net *net, struct vifctl *vifc, int mrtsock)
}
break;
case 0:
- dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
+ if (vifc->vifc_ifindex)
+ dev = dev_get_by_index(net, vifc->vifc_ifindex);
+ else
+ dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
if (!dev)
return -EADDRNOTAVAIL;
err = dev_set_allmulti(dev, 1);
@@ -936,7 +941,7 @@ static void mrtsock_destruct(struct sock *sk)
int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
{
int ret;
- struct vifctl vif;
+ struct vifctln vif;
struct mfcctl mfc;
struct net *net = sock_net(sk);
@@ -975,9 +980,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
return ip_ra_control(sk, 0, NULL);
case MRT_ADD_VIF:
case MRT_DEL_VIF:
- if (optlen != sizeof(vif))
+ /* either vifctl or vifctln is fine; take what we get */
+ if (optlen != sizeof(vif) && optlen != sizeof(struct vifctl))
return -EINVAL;
- if (copy_from_user(&vif, optval, sizeof(vif)))
+ vif.vifc_ifindex = 0;
+ if (copy_from_user(&vif, optval, optlen))
return -EFAULT;
if (vif.vifc_vifi >= MAXVIFS)
return -ENFILE;
--
1.6.5.2