[IPV6]: Add RFC4214 support
authorFred L. Templin <fred.l.templin@boeing.com>
Thu, 29 Nov 2007 11:11:40 +0000 (22:11 +1100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2008 22:55:09 +0000 (14:55 -0800)
This patch includes support for the Intra-Site Automatic Tunnel
Addressing Protocol (ISATAP) per RFC4214. It uses the SIT
module, and is configured using extensions to the "iproute2"
utility. The diffs are specific to the Linux 2.6.24-rc2 kernel
distribution.

This version includes the diff for ./include/linux/if.h which was
missing in the v2.4 submission and is needed to make the
patch compile. The patch has been installed, compiled and
tested in a clean 2.6.24-rc2 kernel build area.

Signed-off-by: Fred L. Templin <fred.l.templin@boeing.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/if.h
include/linux/if_tunnel.h
include/linux/in.h
include/net/addrconf.h
net/ipv6/addrconf.c
net/ipv6/route.c
net/ipv6/sit.c

index 186070d5c5443c1e042c60448154d529a4abaa90..5c9d1fa93fef9f48852ce163f10badd26266f775 100644 (file)
@@ -63,6 +63,7 @@
 #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 IF_GET_IFACE   0x0001          /* for querying only */
 #define IF_GET_PROTO   0x0002
index 660b5010c2d96bdeecc331e635f5375c59937ec5..228eb4eb31295aae0df29a9637e1bf416e086c62 100644 (file)
@@ -17,6 +17,9 @@
 #define GRE_FLAGS      __constant_htons(0x00F8)
 #define GRE_VERSION    __constant_htons(0x0007)
 
+/* i_flags values for SIT mode */
+#define        SIT_ISATAP      0x0001
+
 struct ip_tunnel_parm
 {
        char                    name[IFNAMSIZ];
index 3975cbf52f203116608b7003b1ba61a94afbe3ff..a8f00cac8f7987a08941519546c3b86149d36ffb 100644 (file)
@@ -253,6 +253,14 @@ struct sockaddr_in {
 #define ZERONET(x)     (((x) & htonl(0xff000000)) == htonl(0x00000000))
 #define LOCAL_MCAST(x) (((x) & htonl(0xFFFFFF00)) == htonl(0xE0000000))
 
+/* Special-Use IPv4 Addresses (RFC3330) */
+#define PRIVATE_10(x)  (((x) & htonl(0xff000000)) == htonl(0x0A000000))
+#define LINKLOCAL_169(x) (((x) & htonl(0xffff0000)) == htonl(0xA9FE0000))
+#define PRIVATE_172(x) (((x) & htonl(0xfff00000)) == htonl(0xAC100000))
+#define TEST_192(x)    (((x) & htonl(0xffffff00)) == htonl(0xC0000200))
+#define ANYCAST_6TO4(x)        (((x) & htonl(0xffffff00)) == htonl(0xC0586300))
+#define PRIVATE_192(x) (((x) & htonl(0xffff0000)) == htonl(0xC0A80000))
+#define TEST_198(x)    (((x) & htonl(0xfffe0000)) == htonl(0xC6120000))
 #endif
 
 #endif /* _LINUX_IN_H */
index bccc2feb99da6bf90bbdcbb7d27a9e606cb4b4ef..c56827da0dee8f17fed08a023a236d11c21d15a2 100644 (file)
@@ -17,6 +17,7 @@
 
 #define IPV6_MAX_ADDRESSES             16
 
+#include <linux/in.h>
 #include <linux/in6.h>
 
 struct prefix_info {
@@ -249,6 +250,24 @@ static inline int ipv6_addr_is_ll_all_routers(const struct in6_addr *addr)
                addr->s6_addr32[3] == htonl(0x00000002));
 }
 
+static inline int ipv6_isatap_eui64(u8 *eui, __be32 addr)
+{
+       eui[0] = (ZERONET(addr) || PRIVATE_10(addr) || LOOPBACK(addr) ||
+                 LINKLOCAL_169(addr) || PRIVATE_172(addr) || TEST_192(addr) ||
+                 ANYCAST_6TO4(addr) || PRIVATE_192(addr) || TEST_198(addr) ||
+                 MULTICAST(addr) || BADCLASS(addr)) ? 0x00 : 0x02;
+       eui[1] = 0;
+       eui[2] = 0x5E;
+       eui[3] = 0xFE;
+       memcpy (eui+4, &addr, 4);
+       return 0;
+}
+
+static inline int ipv6_addr_is_isatap(const struct in6_addr *addr)
+{
+       return ((addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE));
+}
+
 #ifdef CONFIG_PROC_FS
 extern int if6_proc_init(void);
 extern void if6_proc_exit(void);
index 6c8b193474ba500b2f0a5a62d2e9d2d590c82e17..f177424c186f3adbb9c1662b66aa11d767f36335 100644 (file)
@@ -377,6 +377,13 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
                       "%s: Disabled Privacy Extensions\n",
                       dev->name);
                ndev->cnf.use_tempaddr = -1;
+
+               if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
+                       printk(KERN_INFO
+                              "%s: Disabled Multicast RS\n",
+                              dev->name);
+                       ndev->cnf.rtr_solicits = 0;
+               }
        } else {
                in6_dev_hold(ndev);
                ipv6_regen_rndid((unsigned long) ndev);
@@ -1409,6 +1416,9 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
                return addrconf_ifid_arcnet(eui, dev);
        case ARPHRD_INFINIBAND:
                return addrconf_ifid_infiniband(eui, dev);
+       case ARPHRD_SIT:
+               if (dev->priv_flags & IFF_ISATAP)
+                       return ipv6_isatap_eui64(eui, *(__be32 *)dev->dev_addr);
        }
        return -1;
 }
@@ -1444,7 +1454,7 @@ regen:
         *
         *  - Reserved subnet anycast (RFC 2526)
         *      11111101 11....11 1xxxxxxx
-        *  - ISATAP (draft-ietf-ngtrans-isatap-13.txt) 5.1
+        *  - ISATAP (RFC4214) 6.1
         *      00-00-5E-FE-xx-xx-xx-xx
         *  - value 0
         *  - XXX: already assigned to an address on the device
@@ -2175,6 +2185,16 @@ static void addrconf_sit_config(struct net_device *dev)
                return;
        }
 
+       if (dev->priv_flags & IFF_ISATAP) {
+               struct in6_addr addr;
+
+               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;
+       }
+
        sit_add_v4_addrs(idev);
 
        if (dev->flags&IFF_POINTOPOINT) {
index d7ec4c9ffc4baa35a091d907652dae923715210d..e2c980dbe52dabcf7d4a0d25a2b930e831948d24 100644 (file)
@@ -1659,6 +1659,8 @@ struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *d
        return rt;
 }
 
+EXPORT_SYMBOL(rt6_get_dflt_router);
+
 struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
                                     struct net_device *dev,
                                     unsigned int pref)
index 71433d29d8846e162f0be47bf200768958bb9a44..b3b8513e9cb7292013c2b548b048dd11bf9bb56e 100644 (file)
@@ -16,6 +16,7 @@
  *     Changes:
  * Roger Venning <r.venning@telstra.com>:      6to4 support
  * Nate Thompson <nate@thebog.net>:            6to4 support
+ * Fred L. Templin <fltemplin@acm.org>:                isatap support
  */
 
 #include <linux/module.h>
@@ -182,6 +183,9 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int
        dev->init = ipip6_tunnel_init;
        nt->parms = *parms;
 
+       if (parms->i_flags & SIT_ISATAP)
+               dev->priv_flags |= IFF_ISATAP;
+
        if (register_netdevice(dev) < 0) {
                free_netdev(dev);
                goto failed;
@@ -364,6 +368,48 @@ static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
                IP6_ECN_set_ce(ipv6_hdr(skb));
 }
 
+/* ISATAP (RFC4214) - check source address */
+static int
+isatap_srcok(struct sk_buff *skb, struct iphdr *iph, struct net_device *dev)
+{
+       struct neighbour *neigh;
+       struct dst_entry *dst;
+       struct rt6_info *rt;
+       struct flowi fl;
+       struct in6_addr *addr6;
+       struct in6_addr rtr;
+       struct ipv6hdr *iph6;
+       int ok = 0;
+
+       /* from onlink default router */
+       ipv6_addr_set(&rtr,  htonl(0xFE800000), 0, 0, 0);
+       ipv6_isatap_eui64(rtr.s6_addr + 8, iph->saddr);
+       if ((rt = rt6_get_dflt_router(&rtr, dev))) {
+               dst_release(&rt->u.dst);
+               return 1;
+       }
+
+       iph6 = ipv6_hdr(skb);
+       memset(&fl, 0, sizeof(fl));
+       fl.proto = iph6->nexthdr;
+       ipv6_addr_copy(&fl.fl6_dst, &iph6->saddr);
+       fl.oif = dev->ifindex;
+       security_skb_classify_flow(skb, &fl);
+
+       dst = ip6_route_output(NULL, &fl);
+       if (!dst->error && (dst->dev == dev) && (neigh = dst->neighbour)) {
+
+               addr6 = (struct in6_addr*)&neigh->primary_key;
+
+               /* from correct previous hop */
+               if (ipv6_addr_is_isatap(addr6) &&
+                   (addr6->s6_addr32[3] == iph->saddr))
+                       ok = 1;
+       }
+       dst_release(dst);
+       return ok;
+}
+
 static int ipip6_rcv(struct sk_buff *skb)
 {
        struct iphdr *iph;
@@ -382,6 +428,14 @@ static int ipip6_rcv(struct sk_buff *skb)
                IPCB(skb)->flags = 0;
                skb->protocol = htons(ETH_P_IPV6);
                skb->pkt_type = PACKET_HOST;
+
+               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;
+               }
                tunnel->stat.rx_packets++;
                tunnel->stat.rx_bytes += skb->len;
                skb->dev = tunnel->dev;
@@ -444,6 +498,29 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (skb->protocol != htons(ETH_P_IPV6))
                goto tx_error;
 
+       /* ISATAP (RFC4214) - 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;
+               }
+
+               addr6 = (struct in6_addr*)&neigh->primary_key;
+               addr_type = ipv6_addr_type(addr6);
+
+               if ((addr_type & IPV6_ADDR_UNICAST) &&
+                    ipv6_addr_is_isatap(addr6))
+                       dst = addr6->s6_addr32[3];
+               else
+                       goto tx_error;
+       }
+
        if (!dst)
                dst = try_6to4(&iph6->daddr);