18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux 38c2ecf20Sopenharmony_ci * Appletalk-IP to IP Decapsulation driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu> 78c2ecf20Sopenharmony_ci * - DDP-IP Decap by: Jay Schulist <jschlst@samba.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Derived from: 108c2ecf20Sopenharmony_ci * - Almost all code already existed in net/appletalk/ddp.c I just 118c2ecf20Sopenharmony_ci * moved/reorginized it into a driver file. Original IP-over-DDP code 128c2ecf20Sopenharmony_ci * was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu> 138c2ecf20Sopenharmony_ci * - skeleton.c: A network driver outline for linux. 148c2ecf20Sopenharmony_ci * Written 1993-94 by Donald Becker. 158c2ecf20Sopenharmony_ci * - dummy.c: A dummy net driver. By Nick Holloway. 168c2ecf20Sopenharmony_ci * - MacGate: A user space Daemon for Appletalk-IP Decap for 178c2ecf20Sopenharmony_ci * Linux by Jay Schulist <jschlst@samba.org> 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Copyright 1993 United States Government as represented by the 208c2ecf20Sopenharmony_ci * Director, National Security Agency. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms 238c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 308c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 318c2ecf20Sopenharmony_ci#include <linux/ip.h> 328c2ecf20Sopenharmony_ci#include <linux/atalk.h> 338c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <net/route.h> 368c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "ipddp.h" /* Our stuff */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n"; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct ipddp_route *ipddp_route_list; 438c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ipddp_route_lock); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#ifdef CONFIG_IPDDP_ENCAP 468c2ecf20Sopenharmony_cistatic int ipddp_mode = IPDDP_ENCAP; 478c2ecf20Sopenharmony_ci#else 488c2ecf20Sopenharmony_cistatic int ipddp_mode = IPDDP_DECAP; 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Index to functions, as function prototypes. */ 528c2ecf20Sopenharmony_cistatic netdev_tx_t ipddp_xmit(struct sk_buff *skb, 538c2ecf20Sopenharmony_ci struct net_device *dev); 548c2ecf20Sopenharmony_cistatic int ipddp_create(struct ipddp_route *new_rt); 558c2ecf20Sopenharmony_cistatic int ipddp_delete(struct ipddp_route *rt); 568c2ecf20Sopenharmony_cistatic struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); 578c2ecf20Sopenharmony_cistatic int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic const struct net_device_ops ipddp_netdev_ops = { 608c2ecf20Sopenharmony_ci .ndo_start_xmit = ipddp_xmit, 618c2ecf20Sopenharmony_ci .ndo_do_ioctl = ipddp_ioctl, 628c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 638c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct net_device * __init ipddp_init(void) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci static unsigned version_printed; 698c2ecf20Sopenharmony_ci struct net_device *dev; 708c2ecf20Sopenharmony_ci int err; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci dev = alloc_etherdev(0); 738c2ecf20Sopenharmony_ci if (!dev) 748c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci netif_keep_dst(dev); 778c2ecf20Sopenharmony_ci strcpy(dev->name, "ipddp%d"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (version_printed++ == 0) 808c2ecf20Sopenharmony_ci printk(version); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Initialize the device structure. */ 838c2ecf20Sopenharmony_ci dev->netdev_ops = &ipddp_netdev_ops; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ 868c2ecf20Sopenharmony_ci dev->mtu = 585; 878c2ecf20Sopenharmony_ci dev->flags |= IFF_NOARP; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * The worst case header we will need is currently a 918c2ecf20Sopenharmony_ci * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) 928c2ecf20Sopenharmony_ci * We send over SNAP so that takes another 8 bytes. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci err = register_netdev(dev); 978c2ecf20Sopenharmony_ci if (err) { 988c2ecf20Sopenharmony_ci free_netdev(dev); 998c2ecf20Sopenharmony_ci return ERR_PTR(err); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Let the user now what mode we are in */ 1038c2ecf20Sopenharmony_ci if(ipddp_mode == IPDDP_ENCAP) 1048c2ecf20Sopenharmony_ci printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 1058c2ecf20Sopenharmony_ci dev->name); 1068c2ecf20Sopenharmony_ci if(ipddp_mode == IPDDP_DECAP) 1078c2ecf20Sopenharmony_ci printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n", 1088c2ecf20Sopenharmony_ci dev->name); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return dev; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Transmit LLAP/ELAP frame using aarp_send_ddp. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct rtable *rtable = skb_rtable(skb); 1208c2ecf20Sopenharmony_ci __be32 paddr = 0; 1218c2ecf20Sopenharmony_ci struct ddpehdr *ddp; 1228c2ecf20Sopenharmony_ci struct ipddp_route *rt; 1238c2ecf20Sopenharmony_ci struct atalk_addr *our_addr; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (rtable->rt_gw_family == AF_INET) 1268c2ecf20Sopenharmony_ci paddr = rtable->rt_gw4; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock(&ipddp_route_lock); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Find appropriate route to use, based only on IP number. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci for(rt = ipddp_route_list; rt != NULL; rt = rt->next) 1348c2ecf20Sopenharmony_ci { 1358c2ecf20Sopenharmony_ci if(rt->ip == paddr) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if(rt == NULL) { 1398c2ecf20Sopenharmony_ci spin_unlock(&ipddp_route_lock); 1408c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci our_addr = atalk_find_dev_addr(rt->dev); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if(ipddp_mode == IPDDP_DECAP) 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Pull off the excess room that should not be there. 1488c2ecf20Sopenharmony_ci * This is due to a hard-header problem. This is the 1498c2ecf20Sopenharmony_ci * quick fix for now though, till it breaks. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Create the Extended DDP header */ 1548c2ecf20Sopenharmony_ci ddp = (struct ddpehdr *)skb->data; 1558c2ecf20Sopenharmony_ci ddp->deh_len_hops = htons(skb->len + (1<<10)); 1568c2ecf20Sopenharmony_ci ddp->deh_sum = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * For Localtalk we need aarp_send_ddp to strip the 1608c2ecf20Sopenharmony_ci * long DDP header and place a shot DDP header on it. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci if(rt->dev->type == ARPHRD_LOCALTLK) 1638c2ecf20Sopenharmony_ci { 1648c2ecf20Sopenharmony_ci ddp->deh_dnet = 0; /* FIXME more hops?? */ 1658c2ecf20Sopenharmony_ci ddp->deh_snet = 0; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ 1708c2ecf20Sopenharmony_ci ddp->deh_snet = our_addr->s_net; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci ddp->deh_dnode = rt->at.s_node; 1738c2ecf20Sopenharmony_ci ddp->deh_snode = our_addr->s_node; 1748c2ecf20Sopenharmony_ci ddp->deh_dport = 72; 1758c2ecf20Sopenharmony_ci ddp->deh_sport = 72; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 1828c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci aarp_send_ddp(rt->dev, skb, &rt->at, NULL); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci spin_unlock(&ipddp_route_lock); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Create a routing entry. We first verify that the 1938c2ecf20Sopenharmony_ci * record does not already exist. If it does we return -EEXIST 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_cistatic int ipddp_create(struct ipddp_route *new_rt) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ipddp_route *rt = kzalloc(sizeof(*rt), GFP_KERNEL); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (rt == NULL) 2008c2ecf20Sopenharmony_ci return -ENOMEM; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci rt->ip = new_rt->ip; 2038c2ecf20Sopenharmony_ci rt->at = new_rt->at; 2048c2ecf20Sopenharmony_ci rt->next = NULL; 2058c2ecf20Sopenharmony_ci if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { 2068c2ecf20Sopenharmony_ci kfree(rt); 2078c2ecf20Sopenharmony_ci return -ENETUNREACH; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci spin_lock_bh(&ipddp_route_lock); 2118c2ecf20Sopenharmony_ci if (__ipddp_find_route(rt)) { 2128c2ecf20Sopenharmony_ci spin_unlock_bh(&ipddp_route_lock); 2138c2ecf20Sopenharmony_ci kfree(rt); 2148c2ecf20Sopenharmony_ci return -EEXIST; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci rt->next = ipddp_route_list; 2188c2ecf20Sopenharmony_ci ipddp_route_list = rt; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_unlock_bh(&ipddp_route_lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* 2268c2ecf20Sopenharmony_ci * Delete a route, we only delete a FULL match. 2278c2ecf20Sopenharmony_ci * If route does not exist we return -ENOENT. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic int ipddp_delete(struct ipddp_route *rt) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct ipddp_route **r = &ipddp_route_list; 2328c2ecf20Sopenharmony_ci struct ipddp_route *tmp; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci spin_lock_bh(&ipddp_route_lock); 2358c2ecf20Sopenharmony_ci while((tmp = *r) != NULL) 2368c2ecf20Sopenharmony_ci { 2378c2ecf20Sopenharmony_ci if(tmp->ip == rt->ip && 2388c2ecf20Sopenharmony_ci tmp->at.s_net == rt->at.s_net && 2398c2ecf20Sopenharmony_ci tmp->at.s_node == rt->at.s_node) 2408c2ecf20Sopenharmony_ci { 2418c2ecf20Sopenharmony_ci *r = tmp->next; 2428c2ecf20Sopenharmony_ci spin_unlock_bh(&ipddp_route_lock); 2438c2ecf20Sopenharmony_ci kfree(tmp); 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci r = &tmp->next; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spin_unlock_bh(&ipddp_route_lock); 2508c2ecf20Sopenharmony_ci return -ENOENT; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * Find a routing entry, we only return a FULL match 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct ipddp_route *f; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci for(f = ipddp_route_list; f != NULL; f = f->next) 2618c2ecf20Sopenharmony_ci { 2628c2ecf20Sopenharmony_ci if(f->ip == rt->ip && 2638c2ecf20Sopenharmony_ci f->at.s_net == rt->at.s_net && 2648c2ecf20Sopenharmony_ci f->at.s_node == rt->at.s_node) 2658c2ecf20Sopenharmony_ci return f; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return NULL; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct ipddp_route __user *rt = ifr->ifr_data; 2748c2ecf20Sopenharmony_ci struct ipddp_route rcp, rcp2, *rp; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if(!capable(CAP_NET_ADMIN)) 2778c2ecf20Sopenharmony_ci return -EPERM; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if(copy_from_user(&rcp, rt, sizeof(rcp))) 2808c2ecf20Sopenharmony_ci return -EFAULT; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci switch(cmd) 2838c2ecf20Sopenharmony_ci { 2848c2ecf20Sopenharmony_ci case SIOCADDIPDDPRT: 2858c2ecf20Sopenharmony_ci return ipddp_create(&rcp); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci case SIOCFINDIPDDPRT: 2888c2ecf20Sopenharmony_ci spin_lock_bh(&ipddp_route_lock); 2898c2ecf20Sopenharmony_ci rp = __ipddp_find_route(&rcp); 2908c2ecf20Sopenharmony_ci if (rp) { 2918c2ecf20Sopenharmony_ci memset(&rcp2, 0, sizeof(rcp2)); 2928c2ecf20Sopenharmony_ci rcp2.ip = rp->ip; 2938c2ecf20Sopenharmony_ci rcp2.at = rp->at; 2948c2ecf20Sopenharmony_ci rcp2.flags = rp->flags; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci spin_unlock_bh(&ipddp_route_lock); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (rp) { 2998c2ecf20Sopenharmony_ci if (copy_to_user(rt, &rcp2, 3008c2ecf20Sopenharmony_ci sizeof(struct ipddp_route))) 3018c2ecf20Sopenharmony_ci return -EFAULT; 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci } else 3048c2ecf20Sopenharmony_ci return -ENOENT; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci case SIOCDELIPDDPRT: 3078c2ecf20Sopenharmony_ci return ipddp_delete(&rcp); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic struct net_device *dev_ipddp; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3178c2ecf20Sopenharmony_cimodule_param(ipddp_mode, int, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int __init ipddp_init_module(void) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci dev_ipddp = ipddp_init(); 3228c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(dev_ipddp); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void __exit ipddp_cleanup_module(void) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct ipddp_route *p; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci unregister_netdev(dev_ipddp); 3308c2ecf20Sopenharmony_ci free_netdev(dev_ipddp); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci while (ipddp_route_list) { 3338c2ecf20Sopenharmony_ci p = ipddp_route_list->next; 3348c2ecf20Sopenharmony_ci kfree(ipddp_route_list); 3358c2ecf20Sopenharmony_ci ipddp_route_list = p; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cimodule_init(ipddp_init_module); 3408c2ecf20Sopenharmony_cimodule_exit(ipddp_cleanup_module); 341