>From 93e78e1d65976643ea8de54cfbe46f5741f49a9d Mon Sep 17 00:00:00 2001 From: David Lamparter 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 --- include/linux/mroute.h | 14 ++++++++++++++ net/ipv4/ipmr.c | 19 +++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 08bc776..0ef73ce 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -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 */ diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 99508d6..9d5d22e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -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