Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
0003  *       Appletalk-IP to IP Decapsulation driver for Linux
0004  *
0005  *  Authors:
0006  *      - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
0007  *  - DDP-IP Decap by: Jay Schulist <jschlst@samba.org>
0008  *
0009  *  Derived from:
0010  *  - Almost all code already existed in net/appletalk/ddp.c I just
0011  *    moved/reorginized it into a driver file. Original IP-over-DDP code
0012  *    was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
0013  *      - skeleton.c: A network driver outline for linux.
0014  *        Written 1993-94 by Donald Becker.
0015  *  - dummy.c: A dummy net driver. By Nick Holloway.
0016  *  - MacGate: A user space Daemon for Appletalk-IP Decap for
0017  *    Linux by Jay Schulist <jschlst@samba.org>
0018  *
0019  *      Copyright 1993 United States Government as represented by the
0020  *      Director, National Security Agency.
0021  *
0022  *      This software may be used and distributed according to the terms
0023  *      of the GNU General Public License, incorporated herein by reference.
0024  */
0025 
0026 #include <linux/compat.h>
0027 #include <linux/module.h>
0028 #include <linux/kernel.h>
0029 #include <linux/init.h>
0030 #include <linux/netdevice.h>
0031 #include <linux/etherdevice.h>
0032 #include <linux/ip.h>
0033 #include <linux/atalk.h>
0034 #include <linux/if_arp.h>
0035 #include <linux/slab.h>
0036 #include <net/route.h>
0037 #include <linux/uaccess.h>
0038 
0039 #include "ipddp.h"      /* Our stuff */
0040 
0041 static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
0042 
0043 static struct ipddp_route *ipddp_route_list;
0044 static DEFINE_SPINLOCK(ipddp_route_lock);
0045 
0046 #ifdef CONFIG_IPDDP_ENCAP
0047 static int ipddp_mode = IPDDP_ENCAP;
0048 #else
0049 static int ipddp_mode = IPDDP_DECAP;
0050 #endif
0051 
0052 /* Index to functions, as function prototypes. */
0053 static netdev_tx_t ipddp_xmit(struct sk_buff *skb,
0054                     struct net_device *dev);
0055 static int ipddp_create(struct ipddp_route *new_rt);
0056 static int ipddp_delete(struct ipddp_route *rt);
0057 static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt);
0058 static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
0059                 void __user *data, int cmd);
0060 
0061 static const struct net_device_ops ipddp_netdev_ops = {
0062     .ndo_start_xmit     = ipddp_xmit,
0063     .ndo_siocdevprivate = ipddp_siocdevprivate,
0064     .ndo_set_mac_address    = eth_mac_addr,
0065     .ndo_validate_addr  = eth_validate_addr,
0066 };
0067 
0068 static struct net_device * __init ipddp_init(void)
0069 {
0070     static unsigned version_printed;
0071     struct net_device *dev;
0072     int err;
0073 
0074     dev = alloc_etherdev(0);
0075     if (!dev)
0076         return ERR_PTR(-ENOMEM);
0077 
0078     netif_keep_dst(dev);
0079     strcpy(dev->name, "ipddp%d");
0080 
0081     if (version_printed++ == 0)
0082                 printk(version);
0083 
0084     /* Initialize the device structure. */
0085     dev->netdev_ops = &ipddp_netdev_ops;
0086 
0087         dev->type = ARPHRD_IPDDP;           /* IP over DDP tunnel */
0088         dev->mtu = 585;
0089         dev->flags |= IFF_NOARP;
0090 
0091         /*
0092          *      The worst case header we will need is currently a
0093          *      ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
0094          *      We send over SNAP so that takes another 8 bytes.
0095          */
0096         dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
0097 
0098     err = register_netdev(dev);
0099     if (err) {
0100         free_netdev(dev);
0101         return ERR_PTR(err);
0102     }
0103 
0104     /* Let the user now what mode we are in */
0105     if(ipddp_mode == IPDDP_ENCAP)
0106         printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 
0107             dev->name);
0108     if(ipddp_mode == IPDDP_DECAP)
0109         printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n", 
0110             dev->name);
0111 
0112         return dev;
0113 }
0114 
0115 
0116 /*
0117  * Transmit LLAP/ELAP frame using aarp_send_ddp.
0118  */
0119 static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
0120 {
0121         struct rtable *rtable = skb_rtable(skb);
0122         __be32 paddr = 0;
0123         struct ddpehdr *ddp;
0124         struct ipddp_route *rt;
0125         struct atalk_addr *our_addr;
0126 
0127     if (rtable->rt_gw_family == AF_INET)
0128         paddr = rtable->rt_gw4;
0129 
0130     spin_lock(&ipddp_route_lock);
0131 
0132     /*
0133          * Find appropriate route to use, based only on IP number.
0134          */
0135         for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
0136         {
0137                 if(rt->ip == paddr)
0138                         break;
0139         }
0140         if(rt == NULL) {
0141         spin_unlock(&ipddp_route_lock);
0142                 return NETDEV_TX_OK;
0143     }
0144 
0145         our_addr = atalk_find_dev_addr(rt->dev);
0146 
0147     if(ipddp_mode == IPDDP_DECAP)
0148         /* 
0149          * Pull off the excess room that should not be there.
0150          * This is due to a hard-header problem. This is the
0151          * quick fix for now though, till it breaks.
0152          */
0153         skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
0154 
0155     /* Create the Extended DDP header */
0156     ddp = (struct ddpehdr *)skb->data;
0157         ddp->deh_len_hops = htons(skb->len + (1<<10));
0158         ddp->deh_sum = 0;
0159 
0160     /*
0161          * For Localtalk we need aarp_send_ddp to strip the
0162          * long DDP header and place a shot DDP header on it.
0163          */
0164         if(rt->dev->type == ARPHRD_LOCALTLK)
0165         {
0166                 ddp->deh_dnet  = 0;   /* FIXME more hops?? */
0167                 ddp->deh_snet  = 0;
0168         }
0169         else
0170         {
0171                 ddp->deh_dnet  = rt->at.s_net;   /* FIXME more hops?? */
0172                 ddp->deh_snet  = our_addr->s_net;
0173         }
0174         ddp->deh_dnode = rt->at.s_node;
0175         ddp->deh_snode = our_addr->s_node;
0176         ddp->deh_dport = 72;
0177         ddp->deh_sport = 72;
0178 
0179         *((__u8 *)(ddp+1)) = 22;            /* ddp type = IP */
0180 
0181         skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
0182 
0183     dev->stats.tx_packets++;
0184     dev->stats.tx_bytes += skb->len;
0185 
0186     aarp_send_ddp(rt->dev, skb, &rt->at, NULL);
0187 
0188     spin_unlock(&ipddp_route_lock);
0189 
0190         return NETDEV_TX_OK;
0191 }
0192 
0193 /*
0194  * Create a routing entry. We first verify that the
0195  * record does not already exist. If it does we return -EEXIST
0196  */
0197 static int ipddp_create(struct ipddp_route *new_rt)
0198 {
0199         struct ipddp_route *rt = kzalloc(sizeof(*rt), GFP_KERNEL);
0200 
0201         if (rt == NULL)
0202                 return -ENOMEM;
0203 
0204         rt->ip = new_rt->ip;
0205         rt->at = new_rt->at;
0206         rt->next = NULL;
0207         if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) {
0208         kfree(rt);
0209                 return -ENETUNREACH;
0210         }
0211 
0212     spin_lock_bh(&ipddp_route_lock);
0213     if (__ipddp_find_route(rt)) {
0214         spin_unlock_bh(&ipddp_route_lock);
0215         kfree(rt);
0216         return -EEXIST;
0217     }
0218 
0219         rt->next = ipddp_route_list;
0220         ipddp_route_list = rt;
0221 
0222     spin_unlock_bh(&ipddp_route_lock);
0223 
0224         return 0;
0225 }
0226 
0227 /*
0228  * Delete a route, we only delete a FULL match.
0229  * If route does not exist we return -ENOENT.
0230  */
0231 static int ipddp_delete(struct ipddp_route *rt)
0232 {
0233         struct ipddp_route **r = &ipddp_route_list;
0234         struct ipddp_route *tmp;
0235 
0236     spin_lock_bh(&ipddp_route_lock);
0237         while((tmp = *r) != NULL)
0238         {
0239                 if(tmp->ip == rt->ip &&
0240            tmp->at.s_net == rt->at.s_net &&
0241            tmp->at.s_node == rt->at.s_node)
0242                 {
0243                         *r = tmp->next;
0244             spin_unlock_bh(&ipddp_route_lock);
0245                         kfree(tmp);
0246                         return 0;
0247                 }
0248                 r = &tmp->next;
0249         }
0250 
0251     spin_unlock_bh(&ipddp_route_lock);
0252         return -ENOENT;
0253 }
0254 
0255 /*
0256  * Find a routing entry, we only return a FULL match
0257  */
0258 static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
0259 {
0260         struct ipddp_route *f;
0261 
0262         for(f = ipddp_route_list; f != NULL; f = f->next)
0263         {
0264                 if(f->ip == rt->ip &&
0265            f->at.s_net == rt->at.s_net &&
0266            f->at.s_node == rt->at.s_node)
0267                         return f;
0268         }
0269 
0270         return NULL;
0271 }
0272 
0273 static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
0274                 void __user *data, int cmd)
0275 {
0276         struct ipddp_route rcp, rcp2, *rp;
0277 
0278     if (in_compat_syscall())
0279         return -EOPNOTSUPP;
0280 
0281         if(!capable(CAP_NET_ADMIN))
0282                 return -EPERM;
0283 
0284     if (copy_from_user(&rcp, data, sizeof(rcp)))
0285         return -EFAULT;
0286 
0287         switch(cmd)
0288         {
0289         case SIOCADDIPDDPRT:
0290                         return ipddp_create(&rcp);
0291 
0292                 case SIOCFINDIPDDPRT:
0293             spin_lock_bh(&ipddp_route_lock);
0294             rp = __ipddp_find_route(&rcp);
0295             if (rp) {
0296                 memset(&rcp2, 0, sizeof(rcp2));
0297                 rcp2.ip    = rp->ip;
0298                 rcp2.at    = rp->at;
0299                 rcp2.flags = rp->flags;
0300             }
0301             spin_unlock_bh(&ipddp_route_lock);
0302 
0303             if (rp) {
0304                 if (copy_to_user(data, &rcp2,
0305                          sizeof(struct ipddp_route)))
0306                     return -EFAULT;
0307                 return 0;
0308             } else
0309                 return -ENOENT;
0310 
0311                 case SIOCDELIPDDPRT:
0312                         return ipddp_delete(&rcp);
0313 
0314                 default:
0315                         return -EINVAL;
0316         }
0317 }
0318 
0319 static struct net_device *dev_ipddp;
0320 
0321 MODULE_LICENSE("GPL");
0322 module_param(ipddp_mode, int, 0);
0323 
0324 static int __init ipddp_init_module(void)
0325 {
0326     dev_ipddp = ipddp_init();
0327     return PTR_ERR_OR_ZERO(dev_ipddp);
0328 }
0329 
0330 static void __exit ipddp_cleanup_module(void)
0331 {
0332         struct ipddp_route *p;
0333 
0334     unregister_netdev(dev_ipddp);
0335         free_netdev(dev_ipddp);
0336 
0337         while (ipddp_route_list) {
0338                 p = ipddp_route_list->next;
0339                 kfree(ipddp_route_list);
0340                 ipddp_route_list = p;
0341         }
0342 }
0343 
0344 module_init(ipddp_init_module);
0345 module_exit(ipddp_cleanup_module);