--- linux-2.6.25.3/include/linux/skbuff.h.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/include/linux/skbuff.h 2008-05-14 15:01:24.000000000 -0700 @@ -310,7 +310,10 @@ struct sk_buff { __u16 tc_verd; /* traffic control verdict */ #endif #endif - /* 2 byte hole */ + __u8 ll_nodetype:2, + ll_noloop:1, + ll_rsvd:5; + /* 8-bit hole */ #ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie; --- linux-2.6.25.3/include/linux/if.h.orig 2008-05-14 14:55:20.000000000 -0700 +++ linux-2.6.25.3/include/linux/if.h 2008-05-14 14:56:03.000000000 -0700 @@ -63,7 +63,9 @@ #define IFF_MASTER_ALB 0x10 /* bonding master, balance-alb. */ #define IFF_BONDING 0x20 /* bonding master or slave */ #define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */ -#define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */ +#define IFF_ISATAP 0x80 /* ISATAP interface (RFC5214) */ +#define IFF_VET 0x0100 /* VET interface (inherits ISATAP) */ +#define IFF_SEAL 0x0200 /* SEAL interface (inherits VET) */ #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 --- linux-2.6.25.3/include/linux/if_tunnel.h.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/include/linux/if_tunnel.h 2008-05-14 14:56:03.000000000 -0700 @@ -19,6 +19,8 @@ /* i_flags values for SIT mode */ #define SIT_ISATAP 0x0001 +#define SIT_VET 0x0002 +#define SIT_SEAL 0x0004 struct ip_tunnel_parm { --- linux-2.6.25.3/include/net/if_inet6.h.orig 2008-05-14 14:55:20.000000000 -0700 +++ linux-2.6.25.3/include/net/if_inet6.h 2008-05-14 14:56:03.000000000 -0700 @@ -286,5 +286,28 @@ static inline void ipv6_ib_mc_map(const buf[9] = broadcast[9]; memcpy(buf + 10, addr->s6_addr + 6, 10); } + +static inline void ipv6_vet_mc_map(struct in6_addr *addr, char *buf) +{ + /* + * (RFC2529, Section 6) - Address Mapping -- Multicast + * + * +-------+-------+-------+-------+ + * | 239 | OLS | DST14 | DST15 | + * +-------+-------+-------+-------+ + * + * DST14, DST15 last two bytes of IPv6 multicast address. + * + * OLS from the configured Organization-Local + * Scope address block (for IPv4 - RFC2365). + * SHOULD be 192, see [ADMIN] for details. + */ + + buf[0]= 239; + buf[1]= 192; + buf[2] = addr->s6_addr[14]; + buf[3] = addr->s6_addr[15]; +} + #endif #endif --- linux-2.6.25.3/net/ipv4/ip_output.c.orig 2008-05-14 14:55:20.000000000 -0700 +++ linux-2.6.25.3/net/ipv4/ip_output.c 2008-05-14 14:56:03.000000000 -0700 @@ -256,7 +256,7 @@ int ip_mc_output(struct sk_buff *skb) */ if (rt->rt_flags&RTCF_MULTICAST) { - if ((!sk || inet_sk(sk)->mc_loop) + if ((!sk || inet_sk(sk)->mc_loop) && !skb->ll_noloop #ifdef CONFIG_IP_MROUTE /* Small optimization: do not loopback not local frames, which returned after forwarding; they will be dropped --- linux-2.6.25.3/net/ipv6/sit.c.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/net/ipv6/sit.c 2008-05-15 09:02:19.000000000 -0700 @@ -16,7 +16,8 @@ * Changes: * Roger Venning : 6to4 support * Nate Thompson : 6to4 support - * Fred L. Templin : isatap support + * Fred Templin : isatap support + * Fred Templin : vet support */ #include @@ -76,7 +77,8 @@ static struct ip_tunnel **tunnels[4] = { static DEFINE_RWLOCK(ipip6_lock); -static struct ip_tunnel * ipip6_tunnel_lookup(__be32 remote, __be32 local) +static struct ip_tunnel * ipip6_tunnel_lookup( + __be32 remote, __be32 local, int ifindex) { unsigned h0 = HASH(remote); unsigned h1 = HASH(local); @@ -92,8 +94,16 @@ static struct ip_tunnel * ipip6_tunnel_l return t; } for (t = tunnels_l[h1]; t; t = t->next) { - if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) - return t; + if (ipv4_is_multicast(local)) { + if ((t->dev->iflink == ifindex) && + (t->dev->flags&IFF_MULTICAST) && + (t->dev->flags&IFF_UP)) + return t; + } else { + if (local == t->parms.iph.saddr && + (t->dev->flags&IFF_UP)) + return t; + } } if ((t = tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP)) return t; @@ -180,8 +190,23 @@ static struct ip_tunnel * ipip6_tunnel_l dev->init = ipip6_tunnel_init; nt->parms = *parms; - if (parms->i_flags & SIT_ISATAP) + /* ISATAP interface family (priv_flags are cumulative) */ + /* + * TODO: handle multiple ISATAP family tunnel interfaces, plus + * multiple underlying IPv4 interfaces per each tunnel interface. + * (Need a *list* of underlying links instead of dev->iflink). + */ + switch (parms->i_flags) { + case SIT_VET: + if (!parms->link) + goto failed_free; + dev->flags |= IFF_MULTICAST; + dev->priv_flags |= IFF_VET; + case SIT_ISATAP: dev->priv_flags |= IFF_ISATAP; + default: + break; + } if (register_netdevice(dev) < 0) goto failed_free; @@ -256,7 +281,8 @@ static int ipip6_err(struct sk_buff *skb err = -ENOENT; read_lock(&ipip6_lock); - t = ipip6_tunnel_lookup(iph->daddr, iph->saddr); + t = ipip6_tunnel_lookup(iph->daddr, + iph->saddr, skb->dev->ifindex); if (t == NULL || t->parms.iph.daddr == 0) goto out; @@ -418,7 +444,8 @@ static int ipip6_rcv(struct sk_buff *skb iph = ip_hdr(skb); read_lock(&ipip6_lock); - if ((tunnel = ipip6_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { + if ((tunnel = ipip6_tunnel_lookup(iph->saddr, + iph->daddr, skb->dev->ifindex)) != NULL) { secpath_reset(skb); skb->mac_header = skb->network_header; skb_reset_network_header(skb); @@ -429,9 +456,14 @@ static int ipip6_rcv(struct sk_buff *skb if ((tunnel->dev->priv_flags & IFF_ISATAP) && !isatap_srcok(skb, iph, tunnel->dev)) { tunnel->stat.rx_errors++; - read_unlock(&ipip6_lock); - kfree_skb(skb); - return 0; + goto drop; + } + + if (ipv4_is_multicast(iph->daddr)) { + if (!(tunnel->dev->flags & IFF_MULTICAST)) + goto drop; + tunnel->stat.multicast++; + skb->pkt_type = PACKET_MULTICAST; } tunnel->stat.rx_packets++; tunnel->stat.rx_bytes += skb->len; @@ -446,6 +478,7 @@ static int ipip6_rcv(struct sk_buff *skb } icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); +drop: read_unlock(&ipip6_lock); out: kfree_skb(skb); @@ -479,7 +512,7 @@ static int ipip6_tunnel_xmit(struct sk_b struct ipv6hdr *iph6 = ipv6_hdr(skb); u8 tos = tunnel->parms.iph.tos; struct rtable *rt; /* Route to the other host */ - struct net_device *tdev; /* Device to other host */ + struct net_device *tdev; /* Device to other host */ struct iphdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; @@ -495,27 +528,45 @@ static int ipip6_tunnel_xmit(struct sk_b if (skb->protocol != htons(ETH_P_IPV6)) goto tx_error; - /* ISATAP (RFC4214) - must come before 6to4 */ + /* ISATAP (RFC5214) - must come before 6to4 */ if (dev->priv_flags & IFF_ISATAP) { struct neighbour *neigh = NULL; - if (skb->dst) - neigh = skb->dst->neighbour; - - if (neigh == NULL) { - if (net_ratelimit()) - printk(KERN_DEBUG "sit: nexthop == NULL\n"); - goto tx_error; + /* + * XXX - this is a mess, and accounts for the fact that + * multicast does not set skb->dst. This is fixed beyond + * 2.6.25, and simply reverts to the pre-vet sit.c code. + */ + if (skb->dst) { + if ((neigh = skb->dst->neighbour) == NULL) { + if (net_ratelimit()) + printk(KERN_DEBUG "sit: nexthop == NULL\n"); + goto tx_error; + } + addr6 = (struct in6_addr*)&neigh->primary_key; + addr_type = ipv6_addr_type(addr6); + } else { + addr6 = &ipv6_hdr(skb)->daddr; + addr_type = ipv6_addr_type(addr6); + if (!(addr_type & IPV6_ADDR_MULTICAST)) { + if (net_ratelimit()) + printk(KERN_DEBUG "sit: skb->dst == NULL\n"); + goto tx_error; + } } - addr6 = (struct in6_addr*)&neigh->primary_key; - addr_type = ipv6_addr_type(addr6); - if ((addr_type & IPV6_ADDR_UNICAST) && - ipv6_addr_is_isatap(addr6)) + ipv6_addr_is_isatap(addr6)) { dst = addr6->s6_addr32[3]; - else - goto tx_error; + } else { + if ((addr_type & IPV6_ADDR_MULTICAST) && + (dev->flags & IFF_MULTICAST)) { + /* skb->ll_noloop suppresses L2 loopback */ + ndisc_mc_map(addr6, (char *)&dst, dev, 0); + skb->ll_noloop = 1; + } else + goto tx_error; + } } if (!dst) @@ -560,9 +611,12 @@ static int ipip6_tunnel_xmit(struct sk_b } } if (rt->rt_type != RTN_UNICAST) { - ip_rt_put(rt); - tunnel->stat.tx_carrier_errors++; - goto tx_error_icmp; + if (!((rt->rt_type == RTN_MULTICAST) && + (dev->flags & IFF_MULTICAST))) { + ip_rt_put(rt); + tunnel->stat.tx_carrier_errors++; + goto tx_error_icmp; + } } tdev = rt->u.dst.dev; @@ -740,6 +794,11 @@ ipip6_tunnel_ioctl (struct net_device *d if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 || p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF))) goto done; + + /* VET must specifiy link for multicast */ + if ((p.i_flags & SIT_VET) && !(p.link)) + goto done; + if (p.iph.ttl) p.iph.frag_off |= htons(IP_DF); @@ -827,6 +886,25 @@ static int ipip6_tunnel_change_mtu(struc return 0; } +static void ipip6_change_rx_flags(struct net_device *dev, int change) +{ + struct ip_tunnel *tunnel; + struct net_device *tdev; + + if (!(dev->priv_flags | IFF_MULTICAST)) + return; + + if (!(tunnel = netdev_priv(dev)) || !tunnel->parms.link) + return; + + tdev = __dev_get_by_index(&init_net, tunnel->parms.link); + + if (change & IFF_ALLMULTI) + dev_set_allmulti(tdev, dev->flags & IFF_ALLMULTI ? 1 : -1); + if (change & IFF_PROMISC) + dev_set_promiscuity(tdev, dev->flags & IFF_PROMISC ? 1 : -1); +} + static void ipip6_tunnel_setup(struct net_device *dev) { dev->uninit = ipip6_tunnel_uninit; @@ -835,6 +913,9 @@ static void ipip6_tunnel_setup(struct ne dev->get_stats = ipip6_tunnel_get_stats; dev->do_ioctl = ipip6_tunnel_ioctl; dev->change_mtu = ipip6_tunnel_change_mtu; + dev->change_rx_flags = ipip6_change_rx_flags; + dev->set_rx_mode = NULL; /* IPv6-IPv4 mapping in mcast.c */ + dev->set_multicast_list = NULL; /* "" */ dev->type = ARPHRD_SIT; dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); --- linux-2.6.25.3/net/ipv6/ndisc.c.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/net/ipv6/ndisc.c 2008-05-14 14:56:03.000000000 -0700 @@ -339,6 +339,12 @@ int ndisc_mc_map(struct in6_addr *addr, case ARPHRD_INFINIBAND: ipv6_ib_mc_map(addr, dev->broadcast, buf); return 0; + case ARPHRD_SIT: + if (dev->priv_flags & IFF_VET) { + ipv6_vet_mc_map(addr, buf); + return 0; + } else + return -EINVAL; default: if (dir) { memcpy(buf, dev->broadcast, dev->addr_len); --- linux-2.6.25.3/net/ipv6/addrconf.c.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/net/ipv6/addrconf.c 2008-05-14 14:56:03.000000000 -0700 @@ -1498,7 +1498,7 @@ regen: * * - Reserved subnet anycast (RFC 2526) * 11111101 11....11 1xxxxxxx - * - ISATAP (RFC4214) 6.1 + * - ISATAP (RFC5214) 6.1 * 00-00-5E-FE-xx-xx-xx-xx * - value 0 * - XXX: already assigned to an address on the device @@ -2238,8 +2238,12 @@ static void addrconf_sit_config(struct n if (dev->priv_flags & IFF_ISATAP) { struct in6_addr addr; + /* Add default multicast route */ + if (dev->priv_flags & IFF_VET) + addrconf_add_mroute(dev); + + /* Add link local address (also adds ll route) */ ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); - addrconf_prefix_route(&addr, 64, dev, 0, 0); if (!ipv6_generate_eui64(addr.s6_addr + 8, dev)) addrconf_add_linklocal(idev, &addr); return; --- linux-2.6.25.3/net/ipv6/mcast.c.orig 2008-05-09 21:48:50.000000000 -0700 +++ linux-2.6.25.3/net/ipv6/mcast.c 2008-05-14 15:32:56.000000000 -0700 @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -707,15 +709,31 @@ static void igmp6_group_added(struct ifm { struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + struct in_device *in_dev; + int add = 0; spin_lock_bh(&mc->mca_lock); if (!(mc->mca_flags&MAF_LOADED)) { mc->mca_flags |= MAF_LOADED; - if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) + if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) { dev_mc_add(dev, buf, dev->addr_len, 0); + add++; + } } spin_unlock_bh(&mc->mca_lock); + /* NB: there may be many IPv6 grps per IPv4 grp */ + if (add && (dev->type == ARPHRD_SIT) && (dev->priv_flags & IFF_VET) && + (in_dev = inetdev_by_index(&init_net, dev->iflink))) { + + if (!(rtnl_is_locked())) { + rtnl_lock(); + ip_mc_inc_group(in_dev, *(__be32 *)buf); + rtnl_unlock(); + } else + ip_mc_inc_group(in_dev, *(__be32 *)buf); + } + if (!(dev->flags & IFF_UP) || (mc->mca_flags & MAF_NOREPORT)) return; @@ -733,12 +751,16 @@ static void igmp6_group_dropped(struct i { struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + struct in_device *in_dev; + int del = 0; spin_lock_bh(&mc->mca_lock); if (mc->mca_flags&MAF_LOADED) { mc->mca_flags &= ~MAF_LOADED; - if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) + if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) { dev_mc_delete(dev, buf, dev->addr_len, 0); + del++; + } } if (mc->mca_flags & MAF_NOREPORT) @@ -754,6 +776,18 @@ static void igmp6_group_dropped(struct i done: ip6_mc_clear_src(mc); spin_unlock_bh(&mc->mca_lock); + + /* NB: there may be many IPv6 grps per IPv4 grp */ + if (del && (dev->type == ARPHRD_SIT) && (dev->priv_flags & IFF_VET) && + (in_dev = inetdev_by_index(&init_net, dev->iflink))) { + + if (!(rtnl_is_locked())) { + rtnl_lock(); + ip_mc_dec_group(in_dev, *(__be32 *)buf); + rtnl_unlock(); + } else + ip_mc_dec_group(in_dev, *(__be32 *)buf); + } } /*