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> 345a0e3ad6STejun Heo #include <linux/slab.h> 351da177e4SLinus Torvalds #include <net/route.h> 367c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds #include "ipddp.h" /* Our stuff */ 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n"; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds static struct ipddp_route *ipddp_route_list; 435615968aSDavid S. Miller static DEFINE_SPINLOCK(ipddp_route_lock); 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds #ifdef CONFIG_IPDDP_ENCAP 461da177e4SLinus Torvalds static int ipddp_mode = IPDDP_ENCAP; 471da177e4SLinus Torvalds #else 481da177e4SLinus Torvalds static int ipddp_mode = IPDDP_DECAP; 491da177e4SLinus Torvalds #endif 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* Index to functions, as function prototypes. */ 520fc48098SStephen Hemminger static netdev_tx_t ipddp_xmit(struct sk_buff *skb, 530fc48098SStephen Hemminger struct net_device *dev); 541da177e4SLinus Torvalds static int ipddp_create(struct ipddp_route *new_rt); 551da177e4SLinus Torvalds static int ipddp_delete(struct ipddp_route *rt); 565615968aSDavid S. Miller static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); 57*dbecb011SArnd Bergmann static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 58*dbecb011SArnd Bergmann void __user *data, int cmd); 591da177e4SLinus Torvalds 6043a67304SStephen Hemminger static const struct net_device_ops ipddp_netdev_ops = { 6143a67304SStephen Hemminger .ndo_start_xmit = ipddp_xmit, 62*dbecb011SArnd Bergmann .ndo_siocdevprivate = ipddp_siocdevprivate, 6343a67304SStephen Hemminger .ndo_set_mac_address = eth_mac_addr, 6443a67304SStephen Hemminger .ndo_validate_addr = eth_validate_addr, 6543a67304SStephen Hemminger }; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds static struct net_device * __init ipddp_init(void) 681da177e4SLinus Torvalds { 691da177e4SLinus Torvalds static unsigned version_printed; 701da177e4SLinus Torvalds struct net_device *dev; 711da177e4SLinus Torvalds int err; 721da177e4SLinus Torvalds 7343a67304SStephen Hemminger dev = alloc_etherdev(0); 741da177e4SLinus Torvalds if (!dev) 751da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 761da177e4SLinus Torvalds 7702875878SEric Dumazet netif_keep_dst(dev); 781da177e4SLinus Torvalds strcpy(dev->name, "ipddp%d"); 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds if (version_printed++ == 0) 811da177e4SLinus Torvalds printk(version); 821da177e4SLinus Torvalds 83421f91d2SUwe Kleine-König /* Initialize the device structure. */ 8443a67304SStephen Hemminger dev->netdev_ops = &ipddp_netdev_ops; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ 871da177e4SLinus Torvalds dev->mtu = 585; 881da177e4SLinus Torvalds dev->flags |= IFF_NOARP; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* 911da177e4SLinus Torvalds * The worst case header we will need is currently a 921da177e4SLinus Torvalds * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) 931da177e4SLinus Torvalds * We send over SNAP so that takes another 8 bytes. 941da177e4SLinus Torvalds */ 951da177e4SLinus Torvalds dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds err = register_netdev(dev); 981da177e4SLinus Torvalds if (err) { 991da177e4SLinus Torvalds free_netdev(dev); 1001da177e4SLinus Torvalds return ERR_PTR(err); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds /* Let the user now what mode we are in */ 1041da177e4SLinus Torvalds if(ipddp_mode == IPDDP_ENCAP) 1051da177e4SLinus Torvalds printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 1061da177e4SLinus Torvalds dev->name); 1071da177e4SLinus Torvalds if(ipddp_mode == IPDDP_DECAP) 1081da177e4SLinus Torvalds printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n", 1091da177e4SLinus Torvalds dev->name); 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds return dev; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds /* 1161da177e4SLinus Torvalds * Transmit LLAP/ELAP frame using aarp_send_ddp. 1171da177e4SLinus Torvalds */ 1180fc48098SStephen Hemminger static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) 1191da177e4SLinus Torvalds { 1201550c171SDavid Ahern struct rtable *rtable = skb_rtable(skb); 1211550c171SDavid Ahern __be32 paddr = 0; 1221da177e4SLinus Torvalds struct ddpehdr *ddp; 1231da177e4SLinus Torvalds struct ipddp_route *rt; 1241da177e4SLinus Torvalds struct atalk_addr *our_addr; 1251da177e4SLinus Torvalds 1261550c171SDavid Ahern if (rtable->rt_gw_family == AF_INET) 1271550c171SDavid Ahern paddr = rtable->rt_gw4; 1281550c171SDavid Ahern 1295615968aSDavid S. Miller spin_lock(&ipddp_route_lock); 1305615968aSDavid S. Miller 1311da177e4SLinus Torvalds /* 1321da177e4SLinus Torvalds * Find appropriate route to use, based only on IP number. 1331da177e4SLinus Torvalds */ 1341da177e4SLinus Torvalds for(rt = ipddp_route_list; rt != NULL; rt = rt->next) 1351da177e4SLinus Torvalds { 1361da177e4SLinus Torvalds if(rt->ip == paddr) 1371da177e4SLinus Torvalds break; 1381da177e4SLinus Torvalds } 1395615968aSDavid S. Miller if(rt == NULL) { 1405615968aSDavid S. Miller spin_unlock(&ipddp_route_lock); 1416ed10654SPatrick McHardy return NETDEV_TX_OK; 1425615968aSDavid S. Miller } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds our_addr = atalk_find_dev_addr(rt->dev); 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds if(ipddp_mode == IPDDP_DECAP) 1471da177e4SLinus Torvalds /* 1481da177e4SLinus Torvalds * Pull off the excess room that should not be there. 1491da177e4SLinus Torvalds * This is due to a hard-header problem. This is the 1501da177e4SLinus Torvalds * quick fix for now though, till it breaks. 1511da177e4SLinus Torvalds */ 1521da177e4SLinus Torvalds skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds /* Create the Extended DDP header */ 1551da177e4SLinus Torvalds ddp = (struct ddpehdr *)skb->data; 1562a50f28cSAl Viro ddp->deh_len_hops = htons(skb->len + (1<<10)); 1571da177e4SLinus Torvalds ddp->deh_sum = 0; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds /* 1601da177e4SLinus Torvalds * For Localtalk we need aarp_send_ddp to strip the 1611da177e4SLinus Torvalds * long DDP header and place a shot DDP header on it. 1621da177e4SLinus Torvalds */ 1631da177e4SLinus Torvalds if(rt->dev->type == ARPHRD_LOCALTLK) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds ddp->deh_dnet = 0; /* FIXME more hops?? */ 1661da177e4SLinus Torvalds ddp->deh_snet = 0; 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds else 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ 1711da177e4SLinus Torvalds ddp->deh_snet = our_addr->s_net; 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds ddp->deh_dnode = rt->at.s_node; 1741da177e4SLinus Torvalds ddp->deh_snode = our_addr->s_node; 1751da177e4SLinus Torvalds ddp->deh_dport = 72; 1761da177e4SLinus Torvalds ddp->deh_sport = 72; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ 1811da177e4SLinus Torvalds 18243a67304SStephen Hemminger dev->stats.tx_packets++; 18343a67304SStephen Hemminger dev->stats.tx_bytes += skb->len; 1841da177e4SLinus Torvalds 185ffcfb8dbSArnaldo Carvalho de Melo aarp_send_ddp(rt->dev, skb, &rt->at, NULL); 1861da177e4SLinus Torvalds 1875615968aSDavid S. Miller spin_unlock(&ipddp_route_lock); 1885615968aSDavid S. Miller 1896ed10654SPatrick McHardy return NETDEV_TX_OK; 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds /* 1931da177e4SLinus Torvalds * Create a routing entry. We first verify that the 1941da177e4SLinus Torvalds * record does not already exist. If it does we return -EEXIST 1951da177e4SLinus Torvalds */ 1961da177e4SLinus Torvalds static int ipddp_create(struct ipddp_route *new_rt) 1971da177e4SLinus Torvalds { 198ce7e40c4SVlad Tsyrklevich struct ipddp_route *rt = kzalloc(sizeof(*rt), GFP_KERNEL); 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds if (rt == NULL) 2011da177e4SLinus Torvalds return -ENOMEM; 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds rt->ip = new_rt->ip; 2041da177e4SLinus Torvalds rt->at = new_rt->at; 2051da177e4SLinus Torvalds rt->next = NULL; 2061da177e4SLinus Torvalds if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { 2071da177e4SLinus Torvalds kfree(rt); 2081da177e4SLinus Torvalds return -ENETUNREACH; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2115615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2125615968aSDavid S. Miller if (__ipddp_find_route(rt)) { 2135615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2141da177e4SLinus Torvalds kfree(rt); 2151da177e4SLinus Torvalds return -EEXIST; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds rt->next = ipddp_route_list; 2191da177e4SLinus Torvalds ipddp_route_list = rt; 2201da177e4SLinus Torvalds 2215615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2225615968aSDavid S. Miller 2231da177e4SLinus Torvalds return 0; 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds /* 2271da177e4SLinus Torvalds * Delete a route, we only delete a FULL match. 2281da177e4SLinus Torvalds * If route does not exist we return -ENOENT. 2291da177e4SLinus Torvalds */ 2301da177e4SLinus Torvalds static int ipddp_delete(struct ipddp_route *rt) 2311da177e4SLinus Torvalds { 2321da177e4SLinus Torvalds struct ipddp_route **r = &ipddp_route_list; 2331da177e4SLinus Torvalds struct ipddp_route *tmp; 2341da177e4SLinus Torvalds 2355615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2361da177e4SLinus Torvalds while((tmp = *r) != NULL) 2371da177e4SLinus Torvalds { 2388e95a202SJoe Perches if(tmp->ip == rt->ip && 2398e95a202SJoe Perches tmp->at.s_net == rt->at.s_net && 2408e95a202SJoe Perches tmp->at.s_node == rt->at.s_node) 2411da177e4SLinus Torvalds { 2421da177e4SLinus Torvalds *r = tmp->next; 2435615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 2441da177e4SLinus Torvalds kfree(tmp); 2451da177e4SLinus Torvalds return 0; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds r = &tmp->next; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 2505615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 251807540baSEric Dumazet return -ENOENT; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds /* 2551da177e4SLinus Torvalds * Find a routing entry, we only return a FULL match 2561da177e4SLinus Torvalds */ 2575615968aSDavid S. Miller static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) 2581da177e4SLinus Torvalds { 2591da177e4SLinus Torvalds struct ipddp_route *f; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds for(f = ipddp_route_list; f != NULL; f = f->next) 2621da177e4SLinus Torvalds { 2638e95a202SJoe Perches if(f->ip == rt->ip && 2648e95a202SJoe Perches f->at.s_net == rt->at.s_net && 2658e95a202SJoe Perches f->at.s_node == rt->at.s_node) 266807540baSEric Dumazet return f; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 269807540baSEric Dumazet return NULL; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 272*dbecb011SArnd Bergmann static int ipddp_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 273*dbecb011SArnd Bergmann void __user *data, int cmd) 2741da177e4SLinus Torvalds { 2755615968aSDavid S. Miller struct ipddp_route rcp, rcp2, *rp; 2761da177e4SLinus Torvalds 277*dbecb011SArnd Bergmann if (in_compat_syscall()) 278*dbecb011SArnd Bergmann return -EOPNOTSUPP; 279*dbecb011SArnd Bergmann 2801da177e4SLinus Torvalds if(!capable(CAP_NET_ADMIN)) 2811da177e4SLinus Torvalds return -EPERM; 2821da177e4SLinus Torvalds 283*dbecb011SArnd Bergmann if (copy_from_user(&rcp, data, sizeof(rcp))) 2841da177e4SLinus Torvalds return -EFAULT; 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds switch(cmd) 2871da177e4SLinus Torvalds { 2881da177e4SLinus Torvalds case SIOCADDIPDDPRT: 289807540baSEric Dumazet return ipddp_create(&rcp); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds case SIOCFINDIPDDPRT: 2925615968aSDavid S. Miller spin_lock_bh(&ipddp_route_lock); 2935615968aSDavid S. Miller rp = __ipddp_find_route(&rcp); 2949824dfaeSWilly Tarreau if (rp) { 2959824dfaeSWilly Tarreau memset(&rcp2, 0, sizeof(rcp2)); 2969824dfaeSWilly Tarreau rcp2.ip = rp->ip; 2979824dfaeSWilly Tarreau rcp2.at = rp->at; 2989824dfaeSWilly Tarreau rcp2.flags = rp->flags; 2999824dfaeSWilly Tarreau } 3005615968aSDavid S. Miller spin_unlock_bh(&ipddp_route_lock); 3015615968aSDavid S. Miller 3025615968aSDavid S. Miller if (rp) { 303*dbecb011SArnd Bergmann if (copy_to_user(data, &rcp2, 3045615968aSDavid S. Miller sizeof(struct ipddp_route))) 3051da177e4SLinus Torvalds return -EFAULT; 3061da177e4SLinus Torvalds return 0; 3075615968aSDavid S. Miller } else 3085615968aSDavid S. Miller return -ENOENT; 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds case SIOCDELIPDDPRT: 311807540baSEric Dumazet return ipddp_delete(&rcp); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds default: 3141da177e4SLinus Torvalds return -EINVAL; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds static struct net_device *dev_ipddp; 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3211da177e4SLinus Torvalds module_param(ipddp_mode, int, 0); 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds static int __init ipddp_init_module(void) 3241da177e4SLinus Torvalds { 3251da177e4SLinus Torvalds dev_ipddp = ipddp_init(); 32681fc9b5cSTonghao Zhang return PTR_ERR_OR_ZERO(dev_ipddp); 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds static void __exit ipddp_cleanup_module(void) 3301da177e4SLinus Torvalds { 3311da177e4SLinus Torvalds struct ipddp_route *p; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds unregister_netdev(dev_ipddp); 3341da177e4SLinus Torvalds free_netdev(dev_ipddp); 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds while (ipddp_route_list) { 3371da177e4SLinus Torvalds p = ipddp_route_list->next; 3381da177e4SLinus Torvalds kfree(ipddp_route_list); 3391da177e4SLinus Torvalds ipddp_route_list = p; 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds module_init(ipddp_init_module); 3441da177e4SLinus Torvalds module_exit(ipddp_cleanup_module); 345