18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* net/atm/clip.c - RFC1577 Classical IP over ATM */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/string.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* for UINT_MAX */ 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <linux/wait.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/if_arp.h> /* for some manifest constants */ 188c2ecf20Sopenharmony_ci#include <linux/notifier.h> 198c2ecf20Sopenharmony_ci#include <linux/atm.h> 208c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 218c2ecf20Sopenharmony_ci#include <linux/atmclip.h> 228c2ecf20Sopenharmony_ci#include <linux/atmarp.h> 238c2ecf20Sopenharmony_ci#include <linux/capability.h> 248c2ecf20Sopenharmony_ci#include <linux/ip.h> /* for net/route.h */ 258c2ecf20Sopenharmony_ci#include <linux/in.h> /* for struct sockaddr_in */ 268c2ecf20Sopenharmony_ci#include <linux/if.h> /* for IFF_UP */ 278c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 288c2ecf20Sopenharmony_ci#include <linux/bitops.h> 298c2ecf20Sopenharmony_ci#include <linux/poison.h> 308c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 318c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 328c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 338c2ecf20Sopenharmony_ci#include <linux/jhash.h> 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <net/route.h> /* for struct rtable and routing */ 368c2ecf20Sopenharmony_ci#include <net/icmp.h> /* icmp_send */ 378c2ecf20Sopenharmony_ci#include <net/arp.h> 388c2ecf20Sopenharmony_ci#include <linux/param.h> /* for HZ */ 398c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 408c2ecf20Sopenharmony_ci#include <asm/byteorder.h> /* for htons etc. */ 418c2ecf20Sopenharmony_ci#include <linux/atomic.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "common.h" 448c2ecf20Sopenharmony_ci#include "resources.h" 458c2ecf20Sopenharmony_ci#include <net/atmclip.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct net_device *clip_devs; 488c2ecf20Sopenharmony_cistatic struct atm_vcc *atmarpd; 498c2ecf20Sopenharmony_cistatic struct timer_list idle_timer; 508c2ecf20Sopenharmony_cistatic const struct neigh_ops clip_neigh_ops; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct sock *sk; 558c2ecf20Sopenharmony_ci struct atmarp_ctrl *ctrl; 568c2ecf20Sopenharmony_ci struct sk_buff *skb; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci pr_debug("(%d)\n", type); 598c2ecf20Sopenharmony_ci if (!atmarpd) 608c2ecf20Sopenharmony_ci return -EUNATCH; 618c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(struct atmarp_ctrl), GFP_ATOMIC); 628c2ecf20Sopenharmony_ci if (!skb) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci ctrl = skb_put(skb, sizeof(struct atmarp_ctrl)); 658c2ecf20Sopenharmony_ci ctrl->type = type; 668c2ecf20Sopenharmony_ci ctrl->itf_num = itf; 678c2ecf20Sopenharmony_ci ctrl->ip = ip; 688c2ecf20Sopenharmony_ci atm_force_charge(atmarpd, skb->truesize); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci sk = sk_atm(atmarpd); 718c2ecf20Sopenharmony_ci skb_queue_tail(&sk->sk_receive_queue, skb); 728c2ecf20Sopenharmony_ci sk->sk_data_ready(sk); 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void link_vcc(struct clip_vcc *clip_vcc, struct atmarp_entry *entry) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci pr_debug("%p to entry %p (neigh %p)\n", clip_vcc, entry, entry->neigh); 798c2ecf20Sopenharmony_ci clip_vcc->entry = entry; 808c2ecf20Sopenharmony_ci clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */ 818c2ecf20Sopenharmony_ci clip_vcc->next = entry->vccs; 828c2ecf20Sopenharmony_ci entry->vccs = clip_vcc; 838c2ecf20Sopenharmony_ci entry->neigh->used = jiffies; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void unlink_clip_vcc(struct clip_vcc *clip_vcc) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct atmarp_entry *entry = clip_vcc->entry; 898c2ecf20Sopenharmony_ci struct clip_vcc **walk; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (!entry) { 928c2ecf20Sopenharmony_ci pr_err("!clip_vcc->entry (clip_vcc %p)\n", clip_vcc); 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ 968c2ecf20Sopenharmony_ci entry->neigh->used = jiffies; 978c2ecf20Sopenharmony_ci for (walk = &entry->vccs; *walk; walk = &(*walk)->next) 988c2ecf20Sopenharmony_ci if (*walk == clip_vcc) { 998c2ecf20Sopenharmony_ci int error; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci *walk = clip_vcc->next; /* atomic */ 1028c2ecf20Sopenharmony_ci clip_vcc->entry = NULL; 1038c2ecf20Sopenharmony_ci if (clip_vcc->xoff) 1048c2ecf20Sopenharmony_ci netif_wake_queue(entry->neigh->dev); 1058c2ecf20Sopenharmony_ci if (entry->vccs) 1068c2ecf20Sopenharmony_ci goto out; 1078c2ecf20Sopenharmony_ci entry->expires = jiffies - 1; 1088c2ecf20Sopenharmony_ci /* force resolution or expiration */ 1098c2ecf20Sopenharmony_ci error = neigh_update(entry->neigh, NULL, NUD_NONE, 1108c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_ADMIN, 0); 1118c2ecf20Sopenharmony_ci if (error) 1128c2ecf20Sopenharmony_ci pr_err("neigh_update failed with %d\n", error); 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci pr_err("ATMARP: failed (entry %p, vcc 0x%p)\n", entry, clip_vcc); 1168c2ecf20Sopenharmony_ciout: 1178c2ecf20Sopenharmony_ci netif_tx_unlock_bh(entry->neigh->dev); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* The neighbour entry n->lock is held. */ 1218c2ecf20Sopenharmony_cistatic int neigh_check_cb(struct neighbour *n) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct atmarp_entry *entry = neighbour_priv(n); 1248c2ecf20Sopenharmony_ci struct clip_vcc *cv; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (n->ops != &clip_neigh_ops) 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci for (cv = entry->vccs; cv; cv = cv->next) { 1298c2ecf20Sopenharmony_ci unsigned long exp = cv->last_use + cv->idle_timeout; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (cv->idle_timeout && time_after(jiffies, exp)) { 1328c2ecf20Sopenharmony_ci pr_debug("releasing vcc %p->%p of entry %p\n", 1338c2ecf20Sopenharmony_ci cv, cv->vcc, entry); 1348c2ecf20Sopenharmony_ci vcc_release_async(cv->vcc, -ETIMEDOUT); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (entry->vccs || time_before(jiffies, entry->expires)) 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (refcount_read(&n->refcnt) > 1) { 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci pr_debug("destruction postponed with ref %d\n", 1458c2ecf20Sopenharmony_ci refcount_read(&n->refcnt)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&n->arp_queue)) != NULL) 1488c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci pr_debug("expired neigh %p\n", n); 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void idle_timer_check(struct timer_list *unused) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci write_lock(&arp_tbl.lock); 1608c2ecf20Sopenharmony_ci __neigh_for_each_release(&arp_tbl, neigh_check_cb); 1618c2ecf20Sopenharmony_ci mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); 1628c2ecf20Sopenharmony_ci write_unlock(&arp_tbl.lock); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int clip_arp_rcv(struct sk_buff *skb) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci pr_debug("\n"); 1708c2ecf20Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 1718c2ecf20Sopenharmony_ci if (!vcc || !atm_charge(vcc, skb->truesize)) { 1728c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci pr_debug("pushing to %p\n", vcc); 1768c2ecf20Sopenharmony_ci pr_debug("using %p\n", CLIP_VCC(vcc)->old_push); 1778c2ecf20Sopenharmony_ci CLIP_VCC(vcc)->old_push(vcc, skb); 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const unsigned char llc_oui[] = { 1828c2ecf20Sopenharmony_ci 0xaa, /* DSAP: non-ISO */ 1838c2ecf20Sopenharmony_ci 0xaa, /* SSAP: non-ISO */ 1848c2ecf20Sopenharmony_ci 0x03, /* Ctrl: Unnumbered Information Command PDU */ 1858c2ecf20Sopenharmony_ci 0x00, /* OUI: EtherType */ 1868c2ecf20Sopenharmony_ci 0x00, 1878c2ecf20Sopenharmony_ci 0x00 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void clip_push(struct atm_vcc *vcc, struct sk_buff *skb) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci pr_debug("\n"); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (!clip_devs) { 1978c2ecf20Sopenharmony_ci atm_return(vcc, skb->truesize); 1988c2ecf20Sopenharmony_ci kfree_skb(skb); 1998c2ecf20Sopenharmony_ci return; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!skb) { 2038c2ecf20Sopenharmony_ci pr_debug("removing VCC %p\n", clip_vcc); 2048c2ecf20Sopenharmony_ci if (clip_vcc->entry) 2058c2ecf20Sopenharmony_ci unlink_clip_vcc(clip_vcc); 2068c2ecf20Sopenharmony_ci clip_vcc->old_push(vcc, NULL); /* pass on the bad news */ 2078c2ecf20Sopenharmony_ci kfree(clip_vcc); 2088c2ecf20Sopenharmony_ci return; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci atm_return(vcc, skb->truesize); 2118c2ecf20Sopenharmony_ci skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs; 2128c2ecf20Sopenharmony_ci /* clip_vcc->entry == NULL if we don't have an IP address yet */ 2138c2ecf20Sopenharmony_ci if (!skb->dev) { 2148c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2158c2ecf20Sopenharmony_ci return; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 2188c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 2198c2ecf20Sopenharmony_ci if (!clip_vcc->encap || 2208c2ecf20Sopenharmony_ci skb->len < RFC1483LLC_LEN || 2218c2ecf20Sopenharmony_ci memcmp(skb->data, llc_oui, sizeof(llc_oui))) 2228c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 2238c2ecf20Sopenharmony_ci else { 2248c2ecf20Sopenharmony_ci skb->protocol = ((__be16 *)skb->data)[3]; 2258c2ecf20Sopenharmony_ci skb_pull(skb, RFC1483LLC_LEN); 2268c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_ARP)) { 2278c2ecf20Sopenharmony_ci skb->dev->stats.rx_packets++; 2288c2ecf20Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 2298c2ecf20Sopenharmony_ci clip_arp_rcv(skb); 2308c2ecf20Sopenharmony_ci return; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci clip_vcc->last_use = jiffies; 2348c2ecf20Sopenharmony_ci skb->dev->stats.rx_packets++; 2358c2ecf20Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 2368c2ecf20Sopenharmony_ci memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data)); 2378c2ecf20Sopenharmony_ci netif_rx(skb); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* 2418c2ecf20Sopenharmony_ci * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that 2428c2ecf20Sopenharmony_ci * clip_pop is atomic with respect to the critical section in clip_start_xmit. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void clip_pop(struct atm_vcc *vcc, struct sk_buff *skb) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 2488c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 2498c2ecf20Sopenharmony_ci int old; 2508c2ecf20Sopenharmony_ci unsigned long flags; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci pr_debug("(vcc %p)\n", vcc); 2538c2ecf20Sopenharmony_ci clip_vcc->old_pop(vcc, skb); 2548c2ecf20Sopenharmony_ci /* skb->dev == NULL in outbound ARP packets */ 2558c2ecf20Sopenharmony_ci if (!dev) 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci spin_lock_irqsave(&PRIV(dev)->xoff_lock, flags); 2588c2ecf20Sopenharmony_ci if (atm_may_send(vcc, 0)) { 2598c2ecf20Sopenharmony_ci old = xchg(&clip_vcc->xoff, 0); 2608c2ecf20Sopenharmony_ci if (old) 2618c2ecf20Sopenharmony_ci netif_wake_queue(dev); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&PRIV(dev)->xoff_lock, flags); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void clip_neigh_solicit(struct neighbour *neigh, struct sk_buff *skb) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci __be32 *ip = (__be32 *) neigh->primary_key; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pr_debug("(neigh %p, skb %p)\n", neigh, skb); 2718c2ecf20Sopenharmony_ci to_atmarpd(act_need, PRIV(neigh->dev)->number, *ip); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci#ifndef CONFIG_ATM_CLIP_NO_ICMP 2778c2ecf20Sopenharmony_ci icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); 2788c2ecf20Sopenharmony_ci#endif 2798c2ecf20Sopenharmony_ci kfree_skb(skb); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic const struct neigh_ops clip_neigh_ops = { 2838c2ecf20Sopenharmony_ci .family = AF_INET, 2848c2ecf20Sopenharmony_ci .solicit = clip_neigh_solicit, 2858c2ecf20Sopenharmony_ci .error_report = clip_neigh_error, 2868c2ecf20Sopenharmony_ci .output = neigh_direct_output, 2878c2ecf20Sopenharmony_ci .connected_output = neigh_direct_output, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int clip_constructor(struct net_device *dev, struct neighbour *neigh) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct atmarp_entry *entry = neighbour_priv(neigh); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (neigh->tbl->family != AF_INET) 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (neigh->type != RTN_UNICAST) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci neigh->nud_state = NUD_NONE; 3018c2ecf20Sopenharmony_ci neigh->ops = &clip_neigh_ops; 3028c2ecf20Sopenharmony_ci neigh->output = neigh->ops->output; 3038c2ecf20Sopenharmony_ci entry->neigh = neigh; 3048c2ecf20Sopenharmony_ci entry->vccs = NULL; 3058c2ecf20Sopenharmony_ci entry->expires = jiffies - 1; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before */ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means 3148c2ecf20Sopenharmony_ci * to allocate the neighbour entry but not to ask atmarpd for resolution. Also, 3158c2ecf20Sopenharmony_ci * don't increment the usage count. This is used to create entries in 3168c2ecf20Sopenharmony_ci * clip_setentry. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int clip_encap(struct atm_vcc *vcc, int mode) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci if (!CLIP_VCC(vcc)) 3228c2ecf20Sopenharmony_ci return -EBADFD; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci CLIP_VCC(vcc)->encap = mode; 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic netdev_tx_t clip_start_xmit(struct sk_buff *skb, 3298c2ecf20Sopenharmony_ci struct net_device *dev) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct clip_priv *clip_priv = PRIV(dev); 3328c2ecf20Sopenharmony_ci struct dst_entry *dst = skb_dst(skb); 3338c2ecf20Sopenharmony_ci struct atmarp_entry *entry; 3348c2ecf20Sopenharmony_ci struct neighbour *n; 3358c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 3368c2ecf20Sopenharmony_ci struct rtable *rt; 3378c2ecf20Sopenharmony_ci __be32 *daddr; 3388c2ecf20Sopenharmony_ci int old; 3398c2ecf20Sopenharmony_ci unsigned long flags; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci pr_debug("(skb %p)\n", skb); 3428c2ecf20Sopenharmony_ci if (!dst) { 3438c2ecf20Sopenharmony_ci pr_err("skb_dst(skb) == NULL\n"); 3448c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3458c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3468c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci rt = (struct rtable *) dst; 3498c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET) 3508c2ecf20Sopenharmony_ci daddr = &rt->rt_gw4; 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci daddr = &ip_hdr(skb)->daddr; 3538c2ecf20Sopenharmony_ci n = dst_neigh_lookup(dst, daddr); 3548c2ecf20Sopenharmony_ci if (!n) { 3558c2ecf20Sopenharmony_ci pr_err("NO NEIGHBOUR !\n"); 3568c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3578c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3588c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci entry = neighbour_priv(n); 3618c2ecf20Sopenharmony_ci if (!entry->vccs) { 3628c2ecf20Sopenharmony_ci if (time_after(jiffies, entry->expires)) { 3638c2ecf20Sopenharmony_ci /* should be resolved */ 3648c2ecf20Sopenharmony_ci entry->expires = jiffies + ATMARP_RETRY_DELAY * HZ; 3658c2ecf20Sopenharmony_ci to_atmarpd(act_need, PRIV(dev)->number, *((__be32 *)n->primary_key)); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci if (entry->neigh->arp_queue.qlen < ATMARP_MAX_UNRES_PACKETS) 3688c2ecf20Sopenharmony_ci skb_queue_tail(&entry->neigh->arp_queue, skb); 3698c2ecf20Sopenharmony_ci else { 3708c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3718c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci goto out_release_neigh; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); 3768c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; 3778c2ecf20Sopenharmony_ci pr_debug("using neighbour %p, vcc %p\n", n, vcc); 3788c2ecf20Sopenharmony_ci if (entry->vccs->encap) { 3798c2ecf20Sopenharmony_ci void *here; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci here = skb_push(skb, RFC1483LLC_LEN); 3828c2ecf20Sopenharmony_ci memcpy(here, llc_oui, sizeof(llc_oui)); 3838c2ecf20Sopenharmony_ci ((__be16 *) here)[3] = skb->protocol; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci atm_account_tx(vcc, skb); 3868c2ecf20Sopenharmony_ci entry->vccs->last_use = jiffies; 3878c2ecf20Sopenharmony_ci pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, vcc, vcc->dev); 3888c2ecf20Sopenharmony_ci old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ 3898c2ecf20Sopenharmony_ci if (old) { 3908c2ecf20Sopenharmony_ci pr_warn("XOFF->XOFF transition\n"); 3918c2ecf20Sopenharmony_ci goto out_release_neigh; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 3948c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 3958c2ecf20Sopenharmony_ci vcc->send(vcc, skb); 3968c2ecf20Sopenharmony_ci if (atm_may_send(vcc, 0)) { 3978c2ecf20Sopenharmony_ci entry->vccs->xoff = 0; 3988c2ecf20Sopenharmony_ci goto out_release_neigh; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci spin_lock_irqsave(&clip_priv->xoff_lock, flags); 4018c2ecf20Sopenharmony_ci netif_stop_queue(dev); /* XOFF -> throttle immediately */ 4028c2ecf20Sopenharmony_ci barrier(); 4038c2ecf20Sopenharmony_ci if (!entry->vccs->xoff) 4048c2ecf20Sopenharmony_ci netif_start_queue(dev); 4058c2ecf20Sopenharmony_ci /* Oh, we just raced with clip_pop. netif_start_queue should be 4068c2ecf20Sopenharmony_ci good enough, because nothing should really be asleep because 4078c2ecf20Sopenharmony_ci of the brief netif_stop_queue. If this isn't true or if it 4088c2ecf20Sopenharmony_ci changes, use netif_wake_queue instead. */ 4098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); 4108c2ecf20Sopenharmony_ciout_release_neigh: 4118c2ecf20Sopenharmony_ci neigh_release(n); 4128c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic int clip_mkip(struct atm_vcc *vcc, int timeout) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct clip_vcc *clip_vcc; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (!vcc->push) 4208c2ecf20Sopenharmony_ci return -EBADFD; 4218c2ecf20Sopenharmony_ci clip_vcc = kmalloc(sizeof(struct clip_vcc), GFP_KERNEL); 4228c2ecf20Sopenharmony_ci if (!clip_vcc) 4238c2ecf20Sopenharmony_ci return -ENOMEM; 4248c2ecf20Sopenharmony_ci pr_debug("%p vcc %p\n", clip_vcc, vcc); 4258c2ecf20Sopenharmony_ci clip_vcc->vcc = vcc; 4268c2ecf20Sopenharmony_ci vcc->user_back = clip_vcc; 4278c2ecf20Sopenharmony_ci set_bit(ATM_VF_IS_CLIP, &vcc->flags); 4288c2ecf20Sopenharmony_ci clip_vcc->entry = NULL; 4298c2ecf20Sopenharmony_ci clip_vcc->xoff = 0; 4308c2ecf20Sopenharmony_ci clip_vcc->encap = 1; 4318c2ecf20Sopenharmony_ci clip_vcc->last_use = jiffies; 4328c2ecf20Sopenharmony_ci clip_vcc->idle_timeout = timeout * HZ; 4338c2ecf20Sopenharmony_ci clip_vcc->old_push = vcc->push; 4348c2ecf20Sopenharmony_ci clip_vcc->old_pop = vcc->pop; 4358c2ecf20Sopenharmony_ci vcc->push = clip_push; 4368c2ecf20Sopenharmony_ci vcc->pop = clip_pop; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* re-process everything received between connection setup and MKIP */ 4398c2ecf20Sopenharmony_ci vcc_process_recv_queue(vcc); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int clip_setentry(struct atm_vcc *vcc, __be32 ip) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct neighbour *neigh; 4478c2ecf20Sopenharmony_ci struct atmarp_entry *entry; 4488c2ecf20Sopenharmony_ci int error; 4498c2ecf20Sopenharmony_ci struct clip_vcc *clip_vcc; 4508c2ecf20Sopenharmony_ci struct rtable *rt; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (vcc->push != clip_push) { 4538c2ecf20Sopenharmony_ci pr_warn("non-CLIP VCC\n"); 4548c2ecf20Sopenharmony_ci return -EBADF; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci clip_vcc = CLIP_VCC(vcc); 4578c2ecf20Sopenharmony_ci if (!ip) { 4588c2ecf20Sopenharmony_ci if (!clip_vcc->entry) { 4598c2ecf20Sopenharmony_ci pr_err("hiding hidden ATMARP entry\n"); 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci pr_debug("remove\n"); 4638c2ecf20Sopenharmony_ci unlink_clip_vcc(clip_vcc); 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci rt = ip_route_output(&init_net, ip, 0, 1, 0); 4678c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 4688c2ecf20Sopenharmony_ci return PTR_ERR(rt); 4698c2ecf20Sopenharmony_ci neigh = __neigh_lookup(&arp_tbl, &ip, rt->dst.dev, 1); 4708c2ecf20Sopenharmony_ci ip_rt_put(rt); 4718c2ecf20Sopenharmony_ci if (!neigh) 4728c2ecf20Sopenharmony_ci return -ENOMEM; 4738c2ecf20Sopenharmony_ci entry = neighbour_priv(neigh); 4748c2ecf20Sopenharmony_ci if (entry != clip_vcc->entry) { 4758c2ecf20Sopenharmony_ci if (!clip_vcc->entry) 4768c2ecf20Sopenharmony_ci pr_debug("add\n"); 4778c2ecf20Sopenharmony_ci else { 4788c2ecf20Sopenharmony_ci pr_debug("update\n"); 4798c2ecf20Sopenharmony_ci unlink_clip_vcc(clip_vcc); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci link_vcc(clip_vcc, entry); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci error = neigh_update(neigh, llc_oui, NUD_PERMANENT, 4848c2ecf20Sopenharmony_ci NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 0); 4858c2ecf20Sopenharmony_ci neigh_release(neigh); 4868c2ecf20Sopenharmony_ci return error; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct net_device_ops clip_netdev_ops = { 4908c2ecf20Sopenharmony_ci .ndo_start_xmit = clip_start_xmit, 4918c2ecf20Sopenharmony_ci .ndo_neigh_construct = clip_constructor, 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void clip_setup(struct net_device *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci dev->netdev_ops = &clip_netdev_ops; 4978c2ecf20Sopenharmony_ci dev->type = ARPHRD_ATM; 4988c2ecf20Sopenharmony_ci dev->neigh_priv_len = sizeof(struct atmarp_entry); 4998c2ecf20Sopenharmony_ci dev->hard_header_len = RFC1483LLC_LEN; 5008c2ecf20Sopenharmony_ci dev->mtu = RFC1626_MTU; 5018c2ecf20Sopenharmony_ci dev->tx_queue_len = 100; /* "normal" queue (packets) */ 5028c2ecf20Sopenharmony_ci /* When using a "real" qdisc, the qdisc determines the queue */ 5038c2ecf20Sopenharmony_ci /* length. tx_queue_len is only used for the default case, */ 5048c2ecf20Sopenharmony_ci /* without any more elaborate queuing. 100 is a reasonable */ 5058c2ecf20Sopenharmony_ci /* compromise between decent burst-tolerance and protection */ 5068c2ecf20Sopenharmony_ci /* against memory hogs. */ 5078c2ecf20Sopenharmony_ci netif_keep_dst(dev); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int clip_create(int number) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct net_device *dev; 5138c2ecf20Sopenharmony_ci struct clip_priv *clip_priv; 5148c2ecf20Sopenharmony_ci int error; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (number != -1) { 5178c2ecf20Sopenharmony_ci for (dev = clip_devs; dev; dev = PRIV(dev)->next) 5188c2ecf20Sopenharmony_ci if (PRIV(dev)->number == number) 5198c2ecf20Sopenharmony_ci return -EEXIST; 5208c2ecf20Sopenharmony_ci } else { 5218c2ecf20Sopenharmony_ci number = 0; 5228c2ecf20Sopenharmony_ci for (dev = clip_devs; dev; dev = PRIV(dev)->next) 5238c2ecf20Sopenharmony_ci if (PRIV(dev)->number >= number) 5248c2ecf20Sopenharmony_ci number = PRIV(dev)->number + 1; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN, 5278c2ecf20Sopenharmony_ci clip_setup); 5288c2ecf20Sopenharmony_ci if (!dev) 5298c2ecf20Sopenharmony_ci return -ENOMEM; 5308c2ecf20Sopenharmony_ci clip_priv = PRIV(dev); 5318c2ecf20Sopenharmony_ci sprintf(dev->name, "atm%d", number); 5328c2ecf20Sopenharmony_ci spin_lock_init(&clip_priv->xoff_lock); 5338c2ecf20Sopenharmony_ci clip_priv->number = number; 5348c2ecf20Sopenharmony_ci error = register_netdev(dev); 5358c2ecf20Sopenharmony_ci if (error) { 5368c2ecf20Sopenharmony_ci free_netdev(dev); 5378c2ecf20Sopenharmony_ci return error; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci clip_priv->next = clip_devs; 5408c2ecf20Sopenharmony_ci clip_devs = dev; 5418c2ecf20Sopenharmony_ci pr_debug("registered (net:%s)\n", dev->name); 5428c2ecf20Sopenharmony_ci return number; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int clip_device_event(struct notifier_block *this, unsigned long event, 5468c2ecf20Sopenharmony_ci void *ptr) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 5518c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (event == NETDEV_UNREGISTER) 5548c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* ignore non-CLIP devices */ 5578c2ecf20Sopenharmony_ci if (dev->type != ARPHRD_ATM || dev->netdev_ops != &clip_netdev_ops) 5588c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci switch (event) { 5618c2ecf20Sopenharmony_ci case NETDEV_UP: 5628c2ecf20Sopenharmony_ci pr_debug("NETDEV_UP\n"); 5638c2ecf20Sopenharmony_ci to_atmarpd(act_up, PRIV(dev)->number, 0); 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci case NETDEV_GOING_DOWN: 5668c2ecf20Sopenharmony_ci pr_debug("NETDEV_DOWN\n"); 5678c2ecf20Sopenharmony_ci to_atmarpd(act_down, PRIV(dev)->number, 0); 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 5708c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 5718c2ecf20Sopenharmony_ci pr_debug("NETDEV_CHANGE*\n"); 5728c2ecf20Sopenharmony_ci to_atmarpd(act_change, PRIV(dev)->number, 0); 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic int clip_inet_event(struct notifier_block *this, unsigned long event, 5798c2ecf20Sopenharmony_ci void *ifa) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct in_device *in_dev; 5828c2ecf20Sopenharmony_ci struct netdev_notifier_info info; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci in_dev = ((struct in_ifaddr *)ifa)->ifa_dev; 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * Transitions are of the down-change-up type, so it's sufficient to 5878c2ecf20Sopenharmony_ci * handle the change on up. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci if (event != NETDEV_UP) 5908c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5918c2ecf20Sopenharmony_ci netdev_notifier_info_init(&info, in_dev->dev); 5928c2ecf20Sopenharmony_ci return clip_device_event(this, NETDEV_CHANGE, &info); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic struct notifier_block clip_dev_notifier = { 5968c2ecf20Sopenharmony_ci .notifier_call = clip_device_event, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic struct notifier_block clip_inet_notifier = { 6028c2ecf20Sopenharmony_ci .notifier_call = clip_inet_event, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void atmarpd_close(struct atm_vcc *vcc) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci pr_debug("\n"); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci rtnl_lock(); 6128c2ecf20Sopenharmony_ci atmarpd = NULL; 6138c2ecf20Sopenharmony_ci skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); 6148c2ecf20Sopenharmony_ci rtnl_unlock(); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci pr_debug("(done)\n"); 6178c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic const struct atmdev_ops atmarpd_dev_ops = { 6218c2ecf20Sopenharmony_ci .close = atmarpd_close 6228c2ecf20Sopenharmony_ci}; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic struct atm_dev atmarpd_dev = { 6268c2ecf20Sopenharmony_ci .ops = &atmarpd_dev_ops, 6278c2ecf20Sopenharmony_ci .type = "arpd", 6288c2ecf20Sopenharmony_ci .number = 999, 6298c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(atmarpd_dev.lock) 6308c2ecf20Sopenharmony_ci}; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int atm_init_atmarp(struct atm_vcc *vcc) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci rtnl_lock(); 6368c2ecf20Sopenharmony_ci if (atmarpd) { 6378c2ecf20Sopenharmony_ci rtnl_unlock(); 6388c2ecf20Sopenharmony_ci return -EADDRINUSE; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci atmarpd = vcc; 6448c2ecf20Sopenharmony_ci set_bit(ATM_VF_META, &vcc->flags); 6458c2ecf20Sopenharmony_ci set_bit(ATM_VF_READY, &vcc->flags); 6468c2ecf20Sopenharmony_ci /* allow replies and avoid getting closed if signaling dies */ 6478c2ecf20Sopenharmony_ci vcc->dev = &atmarpd_dev; 6488c2ecf20Sopenharmony_ci vcc_insert_socket(sk_atm(vcc)); 6498c2ecf20Sopenharmony_ci vcc->push = NULL; 6508c2ecf20Sopenharmony_ci vcc->pop = NULL; /* crash */ 6518c2ecf20Sopenharmony_ci vcc->push_oam = NULL; /* crash */ 6528c2ecf20Sopenharmony_ci rtnl_unlock(); 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 6598c2ecf20Sopenharmony_ci int err = 0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci switch (cmd) { 6628c2ecf20Sopenharmony_ci case SIOCMKCLIP: 6638c2ecf20Sopenharmony_ci case ATMARPD_CTRL: 6648c2ecf20Sopenharmony_ci case ATMARP_MKIP: 6658c2ecf20Sopenharmony_ci case ATMARP_SETENTRY: 6668c2ecf20Sopenharmony_ci case ATMARP_ENCAP: 6678c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 6688c2ecf20Sopenharmony_ci return -EPERM; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci default: 6718c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci switch (cmd) { 6758c2ecf20Sopenharmony_ci case SIOCMKCLIP: 6768c2ecf20Sopenharmony_ci err = clip_create(arg); 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci case ATMARPD_CTRL: 6798c2ecf20Sopenharmony_ci err = atm_init_atmarp(vcc); 6808c2ecf20Sopenharmony_ci if (!err) { 6818c2ecf20Sopenharmony_ci sock->state = SS_CONNECTED; 6828c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci case ATMARP_MKIP: 6868c2ecf20Sopenharmony_ci err = clip_mkip(vcc, arg); 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci case ATMARP_SETENTRY: 6898c2ecf20Sopenharmony_ci err = clip_setentry(vcc, (__force __be32)arg); 6908c2ecf20Sopenharmony_ci break; 6918c2ecf20Sopenharmony_ci case ATMARP_ENCAP: 6928c2ecf20Sopenharmony_ci err = clip_encap(vcc, arg); 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci return err; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic struct atm_ioctl clip_ioctl_ops = { 6998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7008c2ecf20Sopenharmony_ci .ioctl = clip_ioctl, 7018c2ecf20Sopenharmony_ci}; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci static int code[] = { 1, 2, 10, 6, 1, 0 }; 7088c2ecf20Sopenharmony_ci static int e164[] = { 1, 8, 4, 6, 1, 0 }; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (*addr->sas_addr.pub) { 7118c2ecf20Sopenharmony_ci seq_printf(seq, "%s", addr->sas_addr.pub); 7128c2ecf20Sopenharmony_ci if (*addr->sas_addr.prv) 7138c2ecf20Sopenharmony_ci seq_putc(seq, '+'); 7148c2ecf20Sopenharmony_ci } else if (!*addr->sas_addr.prv) { 7158c2ecf20Sopenharmony_ci seq_printf(seq, "%s", "(none)"); 7168c2ecf20Sopenharmony_ci return; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci if (*addr->sas_addr.prv) { 7198c2ecf20Sopenharmony_ci unsigned char *prv = addr->sas_addr.prv; 7208c2ecf20Sopenharmony_ci int *fields; 7218c2ecf20Sopenharmony_ci int i, j; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci fields = *prv == ATM_AFI_E164 ? e164 : code; 7248c2ecf20Sopenharmony_ci for (i = 0; fields[i]; i++) { 7258c2ecf20Sopenharmony_ci for (j = fields[i]; j; j--) 7268c2ecf20Sopenharmony_ci seq_printf(seq, "%02X", *prv++); 7278c2ecf20Sopenharmony_ci if (fields[i + 1]) 7288c2ecf20Sopenharmony_ci seq_putc(seq, '.'); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* This means the neighbour entry has no attached VCC objects. */ 7348c2ecf20Sopenharmony_ci#define SEQ_NO_VCC_TOKEN ((void *) 2) 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic void atmarp_info(struct seq_file *seq, struct neighbour *n, 7378c2ecf20Sopenharmony_ci struct atmarp_entry *entry, struct clip_vcc *clip_vcc) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct net_device *dev = n->dev; 7408c2ecf20Sopenharmony_ci unsigned long exp; 7418c2ecf20Sopenharmony_ci char buf[17]; 7428c2ecf20Sopenharmony_ci int svc, llc, off; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || 7458c2ecf20Sopenharmony_ci (sk_atm(clip_vcc->vcc)->sk_family == AF_ATMSVC)); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || clip_vcc->encap); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (clip_vcc == SEQ_NO_VCC_TOKEN) 7508c2ecf20Sopenharmony_ci exp = entry->neigh->used; 7518c2ecf20Sopenharmony_ci else 7528c2ecf20Sopenharmony_ci exp = clip_vcc->last_use; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci exp = (jiffies - exp) / HZ; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci seq_printf(seq, "%-6s%-4s%-4s%5ld ", 7578c2ecf20Sopenharmony_ci dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci off = scnprintf(buf, sizeof(buf) - 1, "%pI4", n->primary_key); 7608c2ecf20Sopenharmony_ci while (off < 16) 7618c2ecf20Sopenharmony_ci buf[off++] = ' '; 7628c2ecf20Sopenharmony_ci buf[off] = '\0'; 7638c2ecf20Sopenharmony_ci seq_printf(seq, "%s", buf); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (clip_vcc == SEQ_NO_VCC_TOKEN) { 7668c2ecf20Sopenharmony_ci if (time_before(jiffies, entry->expires)) 7678c2ecf20Sopenharmony_ci seq_printf(seq, "(resolving)\n"); 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci seq_printf(seq, "(expired, ref %d)\n", 7708c2ecf20Sopenharmony_ci refcount_read(&entry->neigh->refcnt)); 7718c2ecf20Sopenharmony_ci } else if (!svc) { 7728c2ecf20Sopenharmony_ci seq_printf(seq, "%d.%d.%d\n", 7738c2ecf20Sopenharmony_ci clip_vcc->vcc->dev->number, 7748c2ecf20Sopenharmony_ci clip_vcc->vcc->vpi, clip_vcc->vcc->vci); 7758c2ecf20Sopenharmony_ci } else { 7768c2ecf20Sopenharmony_ci svc_addr(seq, &clip_vcc->vcc->remote); 7778c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistruct clip_seq_state { 7828c2ecf20Sopenharmony_ci /* This member must be first. */ 7838c2ecf20Sopenharmony_ci struct neigh_seq_state ns; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* Local to clip specific iteration. */ 7868c2ecf20Sopenharmony_ci struct clip_vcc *vcc; 7878c2ecf20Sopenharmony_ci}; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e, 7908c2ecf20Sopenharmony_ci struct clip_vcc *curr) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci if (!curr) { 7938c2ecf20Sopenharmony_ci curr = e->vccs; 7948c2ecf20Sopenharmony_ci if (!curr) 7958c2ecf20Sopenharmony_ci return SEQ_NO_VCC_TOKEN; 7968c2ecf20Sopenharmony_ci return curr; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci if (curr == SEQ_NO_VCC_TOKEN) 7998c2ecf20Sopenharmony_ci return NULL; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci curr = curr->next; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return curr; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic void *clip_seq_vcc_walk(struct clip_seq_state *state, 8078c2ecf20Sopenharmony_ci struct atmarp_entry *e, loff_t * pos) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct clip_vcc *vcc = state->vcc; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci vcc = clip_seq_next_vcc(e, vcc); 8128c2ecf20Sopenharmony_ci if (vcc && pos != NULL) { 8138c2ecf20Sopenharmony_ci while (*pos) { 8148c2ecf20Sopenharmony_ci vcc = clip_seq_next_vcc(e, vcc); 8158c2ecf20Sopenharmony_ci if (!vcc) 8168c2ecf20Sopenharmony_ci break; 8178c2ecf20Sopenharmony_ci --(*pos); 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci state->vcc = vcc; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return vcc; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void *clip_seq_sub_iter(struct neigh_seq_state *_state, 8268c2ecf20Sopenharmony_ci struct neighbour *n, loff_t * pos) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct clip_seq_state *state = (struct clip_seq_state *)_state; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (n->dev->type != ARPHRD_ATM) 8318c2ecf20Sopenharmony_ci return NULL; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return clip_seq_vcc_walk(state, neighbour_priv(n), pos); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic void *clip_seq_start(struct seq_file *seq, loff_t * pos) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct clip_seq_state *state = seq->private; 8398c2ecf20Sopenharmony_ci state->ns.neigh_sub_iter = clip_seq_sub_iter; 8408c2ecf20Sopenharmony_ci return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_NEIGH_ONLY); 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int clip_seq_show(struct seq_file *seq, void *v) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci static char atm_arp_banner[] = 8468c2ecf20Sopenharmony_ci "IPitf TypeEncp Idle IP address ATM address\n"; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 8498c2ecf20Sopenharmony_ci seq_puts(seq, atm_arp_banner); 8508c2ecf20Sopenharmony_ci } else { 8518c2ecf20Sopenharmony_ci struct clip_seq_state *state = seq->private; 8528c2ecf20Sopenharmony_ci struct clip_vcc *vcc = state->vcc; 8538c2ecf20Sopenharmony_ci struct neighbour *n = v; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci atmarp_info(seq, n, neighbour_priv(n), vcc); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic const struct seq_operations arp_seq_ops = { 8618c2ecf20Sopenharmony_ci .start = clip_seq_start, 8628c2ecf20Sopenharmony_ci .next = neigh_seq_next, 8638c2ecf20Sopenharmony_ci .stop = neigh_seq_stop, 8648c2ecf20Sopenharmony_ci .show = clip_seq_show, 8658c2ecf20Sopenharmony_ci}; 8668c2ecf20Sopenharmony_ci#endif 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic void atm_clip_exit_noproc(void); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic int __init atm_clip_init(void) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci register_atm_ioctl(&clip_ioctl_ops); 8738c2ecf20Sopenharmony_ci register_netdevice_notifier(&clip_dev_notifier); 8748c2ecf20Sopenharmony_ci register_inetaddr_notifier(&clip_inet_notifier); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci timer_setup(&idle_timer, idle_timer_check, 0); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 8798c2ecf20Sopenharmony_ci { 8808c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci p = proc_create_net("arp", 0444, atm_proc_root, &arp_seq_ops, 8838c2ecf20Sopenharmony_ci sizeof(struct clip_seq_state)); 8848c2ecf20Sopenharmony_ci if (!p) { 8858c2ecf20Sopenharmony_ci pr_err("Unable to initialize /proc/net/atm/arp\n"); 8868c2ecf20Sopenharmony_ci atm_clip_exit_noproc(); 8878c2ecf20Sopenharmony_ci return -ENOMEM; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci#endif 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic void atm_clip_exit_noproc(void) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct net_device *dev, *next; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci unregister_inetaddr_notifier(&clip_inet_notifier); 9008c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&clip_dev_notifier); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci deregister_atm_ioctl(&clip_ioctl_ops); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* First, stop the idle timer, so it stops banging 9058c2ecf20Sopenharmony_ci * on the table. 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_ci del_timer_sync(&idle_timer); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci dev = clip_devs; 9108c2ecf20Sopenharmony_ci while (dev) { 9118c2ecf20Sopenharmony_ci next = PRIV(dev)->next; 9128c2ecf20Sopenharmony_ci unregister_netdev(dev); 9138c2ecf20Sopenharmony_ci free_netdev(dev); 9148c2ecf20Sopenharmony_ci dev = next; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic void __exit atm_clip_exit(void) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci remove_proc_entry("arp", atm_proc_root); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci atm_clip_exit_noproc(); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cimodule_init(atm_clip_init); 9268c2ecf20Sopenharmony_cimodule_exit(atm_clip_exit); 9278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Werner Almesberger"); 9288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Classical/IP over ATM interface"); 9298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 930