11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux 31da177e4SLinus Torvalds * Appletalk-IP to IP Decapsulation driver for Linux 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu> 71da177e4SLinus Torvalds * - DDP-IP Decap by: Jay Schulist <jschlst@samba.org> 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from: 101da177e4SLinus Torvalds * - Almost all code already existed in net/appletalk/ddp.c I just 111da177e4SLinus Torvalds * moved/reorginized it into a driver file. Original IP-over-DDP code 121da177e4SLinus Torvalds * was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu> 131da177e4SLinus Torvalds * - skeleton.c: A network driver outline for linux. 141da177e4SLinus Torvalds * Written 1993-94 by Donald Becker. 151da177e4SLinus Torvalds * - dummy.c: A dummy net driver. By Nick Holloway. 161da177e4SLinus Torvalds * - MacGate: A user space Daemon for Appletalk-IP Decap for 171da177e4SLinus Torvalds * Linux by Jay Schulist <jschlst@samba.org> 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * Copyright 1993 United States Government as represented by the 201da177e4SLinus Torvalds * Director, National Security Agency. 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * This software may be used and distributed according to the terms 231da177e4SLinus Torvalds * of the GNU General Public License, incorporated herein by reference. 241da177e4SLinus Torvalds */ 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds #include <linux/module.h> 271da177e4SLinus Torvalds #include <linux/kernel.h> 281da177e4SLinus Torvalds #include <linux/init.h> 291da177e4SLinus Torvalds #include <linux/netdevice.h> 301da177e4SLinus Torvalds #include <linux/etherdevice.h> 311da177e4SLinus Torvalds #include <linux/ip.h> 321da177e4SLinus Torvalds #include <linux/atalk.h> 331da177e4SLinus Torvalds #include <linux/if_arp.h> 341da177e4SLinus Torvalds #include <net/route.h> 351da177e4SLinus Torvalds #include <asm/uaccess.h> 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds #include "ipddp.h" /* Our stuff */ 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n"; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds static struct ipddp_route *ipddp_route_list; 425615968aSDavid S. Miller static DEFINE_SPINLOCK(ipddp_route_lock); 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #ifdef CONFIG_IPDDP_ENCAP 451da177e4SLinus Torvalds static int ipddp_mode = IPDDP_ENCAP; 461da177e4SLinus Torvalds #else 471da177e4SLinus Torvalds static int ipddp_mode = IPDDP_DECAP; 481da177e4SLinus Torvalds #endif 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* Index to functions, as function prototypes. */ 511da177e4SLinus Torvalds static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev); 521da177e4SLinus Torvalds static int ipddp_create(struct ipddp_route *new_rt); 531da177e4SLinus Torvalds static int ipddp_delete(struct ipddp_route *rt); 545615968aSDavid S. Miller static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); 551da177e4SLinus Torvalds static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); 561da177e4SLinus Torvalds 5743a67304SStephen Hemminger static const struct net_device_ops ipddp_netdev_ops = { 5843a67304SStephen Hemminger .ndo_start_xmit = ipddp_xmit, 5943a67304SStephen Hemminger .ndo_do_ioctl = ipddp_ioctl, 6043a67304SStephen Hemminger .ndo_change_mtu = eth_change_mtu, 6143a67304SStephen Hemminger .ndo_set_mac_address = eth_mac_addr, 6243a67304SStephen Hemminger .ndo_validate_addr = eth_validate_addr, 6343a67304SStephen Hemminger }; 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static struct net_device * __init ipddp_init(void) 661da177e4SLinus Torvalds { 671da177e4SLinus Torvalds static unsigned version_printed; 681da177e4SLinus Torvalds struct net_device *dev; 691da177e4SLinus Torvalds int err; 701da177e4SLinus Torvalds 7143a67304SStephen Hemminger dev = alloc_etherdev(0); 721da177e4SLinus Torvalds if (!dev) 731da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 741da177e4SLinus Torvalds 7593f154b5SEric Dumazet dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; 761da177e4SLinus Torvalds strcpy(dev->name, "ipddp%d"); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds if (version_printed++ == 0) 791da177e4SLinus Torvalds printk(version); 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds /* Initalize the device structure. */ 8243a67304SStephen Hemminger dev->netdev_ops = &ipddp_netdev_ops; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ 851da177e4SLinus Torvalds dev->mtu = 585; 861da177e4SLinus Torvalds dev->flags |= IFF_NOARP; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* 891da177e4SLinus Torvalds * The worst case header we will need is currently a 901da177e4SLinus Torvalds * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) 911da177e4SLinus Torvalds * We send over SNAP so that takes another 8 bytes. 921da177e4SLinus Torvalds */ 931da177e4SLinus Torvalds dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds err = register_netdev(dev); 961da177e4SLinus Torvalds if (err) { 971da177e4SLinus Torvalds free_netdev(dev); 981da177e4SLinus Torvalds return ERR_PTR(err); 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds /* Let the user now what mode we are in */ 1021da177e4SLinus Torvalds if(ipddp_mode == IPDDP_ENCAP) 1031da177e4SLinus Torvalds printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 1041da177e4SLinus Torvalds dev->name); 1051da177e4SLinus Torvalds if(ipddp_mode == IPDDP_DECAP) 1061da177e4SLinus Torvalds printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n", 1071da177e4SLinus Torvalds dev->name); 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds return dev; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* 1141da177e4SLinus Torvalds * Transmit LLAP/ELAP frame using aarp_send_ddp. 1151da177e4SLinus Torvalds */ 1161da177e4SLinus Torvalds static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) 1171da177e4SLinus Torvalds { 118511c3f92SEric Dumazet __be32 paddr = skb_rtable(skb)->rt_gateway; 1191da177e4SLinus Torvalds struct ddpehdr *ddp; 1201da177e4SLinus Torvalds struct ipddp_route *rt; 1211da177e4SLinus Torvalds struct atalk_addr *our_addr; 1221da177e4SLinus Torvalds 1235615968aSDavid S. Miller spin_lock(&ipddp_route_lock); 1245615968aSDavid S. Miller 1251da177e4SLinus Torvalds /* 1261da177e4SLinus Torvalds * Find appropriate route to use, based only on IP number. 1271da177e4SLinus Torvalds */ 1281da177e4SLinus Torvalds for(rt = ipddp_route_list; rt != NULL; rt = rt->next) 1291da177e4SLinus Torvalds { 1301da177e4SLinus Torvalds if(rt->ip == paddr) 1311da177e4SLinus Torvalds break; 1321da177e4SLinus Torvalds } 1335615968aSDavid S. Miller if(rt == NULL) { 1345615968aSDavid S. Miller spin_unlock(&ipddp_route_lock); 1356ed10654SPatrick McHardy return NETDEV_TX_OK; 1365615968aSDavid S. Miller } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds our_addr = atalk_find_dev_addr(rt->dev); 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds if(ipddp_mode == IPDDP_DECAP) 1411da177e4SLinus Torvalds /* 1421da177e4SLinus Torvalds * Pull off the excess room that should not be there. 1431da177e4SLinus Torvalds * This is due to a hard-header problem. This is the 1441da177e4SLinus Torvalds * quick fix for now though, till it breaks. 1451da177e4SLinus Torvalds */ 1461da177e4SLinus Torvalds skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds /* Create the Extended DDP header */ 1491da177e4SLinus Torvalds ddp = (struct ddpehdr *)skb->data; 1502a50f28cSAl Viro ddp->deh_len_hops = htons(skb->len + (1<<10)); 1511da177e4SLinus Torvalds ddp->deh_sum = 0; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds /* 1541da177e4SLinus Torvalds * For Localtalk we need aarp_send_ddp to strip the 1551da177e4SLinus Torvalds * long DDP header and place a shot DDP header on it. 1561da177e4SLinus Torvalds */ 1571da177e4SLinus Torvalds if(rt->dev->type == ARPHRD_LOCALTLK) 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds ddp->deh_dnet = 0; /* FIXME more hops?? */ 1601da177e4SLinus Torvalds ddp->deh_snet = 0; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds else 1631da177e4SLinus Torvalds { 1641da177e4SLinus Torvalds ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ 1651da177e4SLinus Torvalds ddp->deh_snet = our_addr->s_net; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds ddp->deh_dnode = rt->at.s_node; 1681da177e4SLinus Torvalds ddp->deh_snode = our_addr->s_node; 1691da177e4SLinus Torvalds ddp->deh_dport = 72; 1701da177e4SLinus Torvalds ddp->deh_sport = 72; 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ 1751da177e4SLinus Torvalds 17643a67304SStephen Hemminger dev->stats.tx_packets++; 17743a67304SStephen Hemminger dev->stats.tx_bytes += skb->len; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0) 1801da177e4SLinus Torvalds dev_kfree_skb(skb); 1811da177e4SLinus Torvalds 1825615968aSDavid S. Miller spin_unlock(&ipddp_route_lock); 1835615968aSDavid S. Miller 1846ed10654SPatrick McHardy return NETDEV_TX_OK; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* 1881da177e4SLinus Torvalds * Create a routing entry. We first verify that the 1891da177e4SLinus Torvalds * record does not already exist. If it does we return -EEXIST 1901da177e4SLinus Torvalds */ 1911da177e4SLinus Torvalds static int ipddp_create(struct ipddp_route *new_rt) 1921da177e4SLinus Torvalds { 1935cbded58SRobert P. J. Day struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds if (rt == NULL) 1961da177e4SLinus Torvalds return -ENOMEM; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds rt->ip = new_rt->ip; 1991da177e4SLinus Torvalds rt->at = new_rt->at; 2001da177e4SLinus Torvalds rt->next = NULL; 2011da177e4SLinus Torvalds if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { 2021da177e4SLinus Torvalds kfree(rt); 2031da177e4SLinus Torvalds return -ENETUNREACH; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2065615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2075615968aSDavid S. Miller if (__ipddp_find_route(rt)) { 2085615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2091da177e4SLinus Torvalds kfree(rt); 2101da177e4SLinus Torvalds return -EEXIST; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds rt->next = ipddp_route_list; 2141da177e4SLinus Torvalds ipddp_route_list = rt; 2151da177e4SLinus Torvalds 2165615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2175615968aSDavid S. Miller 2181da177e4SLinus Torvalds return 0; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds /* 2221da177e4SLinus Torvalds * Delete a route, we only delete a FULL match. 2231da177e4SLinus Torvalds * If route does not exist we return -ENOENT. 2241da177e4SLinus Torvalds */ 2251da177e4SLinus Torvalds static int ipddp_delete(struct ipddp_route *rt) 2261da177e4SLinus Torvalds { 2271da177e4SLinus Torvalds struct ipddp_route **r = &ipddp_route_list; 2281da177e4SLinus Torvalds struct ipddp_route *tmp; 2291da177e4SLinus Torvalds 2305615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2311da177e4SLinus Torvalds while((tmp = *r) != NULL) 2321da177e4SLinus Torvalds { 2331da177e4SLinus Torvalds if(tmp->ip == rt->ip 2341da177e4SLinus Torvalds && tmp->at.s_net == rt->at.s_net 2351da177e4SLinus Torvalds && tmp->at.s_node == rt->at.s_node) 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds *r = tmp->next; 2385615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2391da177e4SLinus Torvalds kfree(tmp); 2401da177e4SLinus Torvalds return 0; 2411da177e4SLinus Torvalds } 2421da177e4SLinus Torvalds r = &tmp->next; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 2455615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2461da177e4SLinus Torvalds return (-ENOENT); 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds /* 2501da177e4SLinus Torvalds * Find a routing entry, we only return a FULL match 2511da177e4SLinus Torvalds */ 2525615968aSDavid S. Miller static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds struct ipddp_route *f; 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds for(f = ipddp_route_list; f != NULL; f = f->next) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds if(f->ip == rt->ip 2591da177e4SLinus Torvalds && f->at.s_net == rt->at.s_net 2601da177e4SLinus Torvalds && f->at.s_node == rt->at.s_node) 2611da177e4SLinus Torvalds return (f); 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds return (NULL); 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds struct ipddp_route __user *rt = ifr->ifr_data; 2705615968aSDavid S. Miller struct ipddp_route rcp, rcp2, *rp; 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds if(!capable(CAP_NET_ADMIN)) 2731da177e4SLinus Torvalds return -EPERM; 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds if(copy_from_user(&rcp, rt, sizeof(rcp))) 2761da177e4SLinus Torvalds return -EFAULT; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds switch(cmd) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds case SIOCADDIPDDPRT: 2811da177e4SLinus Torvalds return (ipddp_create(&rcp)); 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds case SIOCFINDIPDDPRT: 2845615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2855615968aSDavid S. Miller rp = __ipddp_find_route(&rcp); 2865615968aSDavid S. Miller if (rp) 2875615968aSDavid S. Miller memcpy(&rcp2, rp, sizeof(rcp2)); 2885615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2895615968aSDavid S. Miller 2905615968aSDavid S. Miller if (rp) { 2915615968aSDavid S. Miller if (copy_to_user(rt, &rcp2, 2925615968aSDavid S. Miller sizeof(struct ipddp_route))) 2931da177e4SLinus Torvalds return -EFAULT; 2941da177e4SLinus Torvalds return 0; 2955615968aSDavid S. Miller } else 2965615968aSDavid S. Miller return -ENOENT; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds case SIOCDELIPDDPRT: 2991da177e4SLinus Torvalds return (ipddp_delete(&rcp)); 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds default: 3021da177e4SLinus Torvalds return -EINVAL; 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds static struct net_device *dev_ipddp; 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3091da177e4SLinus Torvalds module_param(ipddp_mode, int, 0); 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds static int __init ipddp_init_module(void) 3121da177e4SLinus Torvalds { 3131da177e4SLinus Torvalds dev_ipddp = ipddp_init(); 3141da177e4SLinus Torvalds if (IS_ERR(dev_ipddp)) 3151da177e4SLinus Torvalds return PTR_ERR(dev_ipddp); 3161da177e4SLinus Torvalds return 0; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds static void __exit ipddp_cleanup_module(void) 3201da177e4SLinus Torvalds { 3211da177e4SLinus Torvalds struct ipddp_route *p; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds unregister_netdev(dev_ipddp); 3241da177e4SLinus Torvalds free_netdev(dev_ipddp); 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds while (ipddp_route_list) { 3271da177e4SLinus Torvalds p = ipddp_route_list->next; 3281da177e4SLinus Torvalds kfree(ipddp_route_list); 3291da177e4SLinus Torvalds ipddp_route_list = p; 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds module_init(ipddp_init_module); 3341da177e4SLinus Torvalds module_exit(ipddp_cleanup_module); 335