📄 clip.c
字号:
/* net/atm/clip.c - RFC1577 Classical IP over ATM *//* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */#include <linux/config.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/kernel.h> /* for UINT_MAX */#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/wait.h>#include <linux/timer.h>#include <linux/if_arp.h> /* for some manifest constants */#include <linux/notifier.h>#include <linux/atm.h>#include <linux/atmdev.h>#include <linux/atmclip.h>#include <linux/atmarp.h>#include <linux/ip.h> /* for net/route.h */#include <linux/in.h> /* for struct sockaddr_in */#include <linux/if.h> /* for IFF_UP */#include <linux/inetdevice.h>#include <linux/bitops.h>#include <net/route.h> /* for struct rtable and routing */#include <net/icmp.h> /* icmp_send */#include <asm/param.h> /* for HZ */#include <asm/byteorder.h> /* for htons etc. */#include <asm/system.h> /* save/restore_flags */#include <asm/uaccess.h>#include <asm/atomic.h>#include "common.h"#include "resources.h"#include "ipcommon.h"#include <net/atmclip.h>#if 0#define DPRINTK(format,args...) printk(format,##args)#else#define DPRINTK(format,args...)#endifstruct net_device *clip_devs = NULL;struct atm_vcc *atmarpd = NULL;static struct timer_list idle_timer;static int start_timer = 1;static int to_atmarpd(enum atmarp_ctrl_type type,int itf,unsigned long ip){ struct atmarp_ctrl *ctrl; struct sk_buff *skb; DPRINTK("to_atmarpd(%d)\n",type); if (!atmarpd) return -EUNATCH; skb = alloc_skb(sizeof(struct atmarp_ctrl),GFP_ATOMIC); if (!skb) return -ENOMEM; ctrl = (struct atmarp_ctrl *) skb_put(skb,sizeof(struct atmarp_ctrl)); ctrl->type = type; ctrl->itf_num = itf; ctrl->ip = ip; atm_force_charge(atmarpd,skb->truesize); skb_queue_tail(&atmarpd->recvq,skb); wake_up(&atmarpd->sleep); return 0;}static void link_vcc(struct clip_vcc *clip_vcc,struct atmarp_entry *entry){ DPRINTK("link_vcc %p to entry %p (neigh %p)\n",clip_vcc,entry, entry->neigh); clip_vcc->entry = entry; clip_vcc->xoff = 0; /* @@@ may overrun buffer by one packet */ clip_vcc->next = entry->vccs; entry->vccs = clip_vcc; entry->neigh->used = jiffies;}static void unlink_clip_vcc(struct clip_vcc *clip_vcc){ struct atmarp_entry *entry = clip_vcc->entry; struct clip_vcc **walk; if (!entry) { printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc); return; } entry->neigh->used = jiffies; for (walk = &entry->vccs; *walk; walk = &(*walk)->next) if (*walk == clip_vcc) { int error; *walk = clip_vcc->next; /* atomic */ clip_vcc->entry = NULL; if (clip_vcc->xoff) netif_wake_queue(entry->neigh->dev); if (entry->vccs) return; entry->expires = jiffies-1; /* force resolution or expiration */ error = neigh_update(entry->neigh,NULL,NUD_NONE,0,0); if (error) printk(KERN_CRIT "unlink_clip_vcc: " "neigh_update failed with %d\n",error); return; } printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc " "0x%p)\n",entry,clip_vcc);}static void idle_timer_check(unsigned long dummy){ int i; /*DPRINTK("idle_timer_check\n");*/ write_lock(&clip_tbl.lock); for (i = 0; i <= NEIGH_HASHMASK; i++) { struct neighbour **np; for (np = &clip_tbl.hash_buckets[i]; *np;) { struct neighbour *n = *np; struct atmarp_entry *entry = NEIGH2ENTRY(n); struct clip_vcc *clip_vcc; for (clip_vcc = entry->vccs; clip_vcc; clip_vcc = clip_vcc->next) if (clip_vcc->idle_timeout && time_after(jiffies, clip_vcc->last_use+ clip_vcc->idle_timeout)) { DPRINTK("releasing vcc %p->%p of " "entry %p\n",clip_vcc,clip_vcc->vcc, entry); atm_async_release_vcc(clip_vcc->vcc, -ETIMEDOUT); } if (entry->vccs || time_before(jiffies, entry->expires)) { np = &n->next; continue; } if (atomic_read(&n->refcnt) > 1) { struct sk_buff *skb; DPRINTK("destruction postponed with ref %d\n", atomic_read(&n->refcnt)); while ((skb = skb_dequeue(&n->arp_queue)) != NULL) dev_kfree_skb(skb); np = &n->next; continue; } *np = n->next; DPRINTK("expired neigh %p\n",n); n->dead = 1; neigh_release(n); } } mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ); write_unlock(&clip_tbl.lock);}static int clip_arp_rcv(struct sk_buff *skb){ struct atm_vcc *vcc; DPRINTK("clip_arp_rcv\n"); vcc = ATM_SKB(skb)->vcc; if (!vcc || !atm_charge(vcc,skb->truesize)) { dev_kfree_skb_any(skb); return 0; } DPRINTK("pushing to %p\n",vcc); DPRINTK("using %p\n",CLIP_VCC(vcc)->old_push); CLIP_VCC(vcc)->old_push(vcc,skb); return 0;}void clip_push(struct atm_vcc *vcc,struct sk_buff *skb){ struct clip_vcc *clip_vcc = CLIP_VCC(vcc); DPRINTK("clip push\n"); if (!skb) { DPRINTK("removing VCC %p\n",clip_vcc); if (clip_vcc->entry) unlink_clip_vcc(clip_vcc); clip_vcc->old_push(vcc,NULL); /* pass on the bad news */ kfree(clip_vcc); return; } atm_return(vcc,skb->truesize); skb->dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : clip_devs; /* clip_vcc->entry == NULL if we don't have an IP address yet */ if (!skb->dev) { dev_kfree_skb_any(skb); return; } ATM_SKB(skb)->vcc = vcc; skb->mac.raw = skb->data; if (!clip_vcc->encap || skb->len < RFC1483LLC_LEN || memcmp(skb->data, llc_oui,sizeof(llc_oui))) skb->protocol = htons(ETH_P_IP); else { skb->protocol = ((u16 *) skb->data)[3]; skb_pull(skb,RFC1483LLC_LEN); if (skb->protocol == htons(ETH_P_ARP)) { PRIV(skb->dev)->stats.rx_packets++; PRIV(skb->dev)->stats.rx_bytes += skb->len; clip_arp_rcv(skb); return; } } clip_vcc->last_use = jiffies; PRIV(skb->dev)->stats.rx_packets++; PRIV(skb->dev)->stats.rx_bytes += skb->len; netif_rx(skb);}/* * Note: these spinlocks _must_not_ block on non-SMP. The only goal is that * clip_pop is atomic with respect to the critical section in clip_start_xmit. */static void clip_pop(struct atm_vcc *vcc,struct sk_buff *skb){ struct clip_vcc *clip_vcc = CLIP_VCC(vcc); struct net_device *dev = skb->dev; int old; unsigned long flags; DPRINTK("clip_pop(vcc %p)\n",vcc); clip_vcc->old_pop(vcc,skb); /* skb->dev == NULL in outbound ARP packets */ if (!dev) return; spin_lock_irqsave(&PRIV(dev)->xoff_lock,flags); if (atm_may_send(vcc,0)) { old = xchg(&clip_vcc->xoff,0); if (old) netif_wake_queue(dev); } spin_unlock_irqrestore(&PRIV(dev)->xoff_lock,flags);}static void clip_neigh_destroy(struct neighbour *neigh){ DPRINTK("clip_neigh_destroy (neigh %p)\n",neigh); if (NEIGH2ENTRY(neigh)->vccs) printk(KERN_CRIT "clip_neigh_destroy: vccs != NULL !!!\n"); NEIGH2ENTRY(neigh)->vccs = (void *) 0xdeadbeef;}static void clip_neigh_solicit(struct neighbour *neigh,struct sk_buff *skb){ DPRINTK("clip_neigh_solicit (neigh %p, skb %p)\n",neigh,skb); to_atmarpd(act_need,PRIV(neigh->dev)->number,NEIGH2ENTRY(neigh)->ip);}static void clip_neigh_error(struct neighbour *neigh,struct sk_buff *skb){#ifndef CONFIG_ATM_CLIP_NO_ICMP icmp_send(skb,ICMP_DEST_UNREACH,ICMP_HOST_UNREACH,0);#endif kfree_skb(skb);}static struct neigh_ops clip_neigh_ops = { family: AF_INET, destructor: clip_neigh_destroy, solicit: clip_neigh_solicit, error_report: clip_neigh_error, output: dev_queue_xmit, connected_output: dev_queue_xmit, hh_output: dev_queue_xmit, queue_xmit: dev_queue_xmit,};static int clip_constructor(struct neighbour *neigh){ struct atmarp_entry *entry = NEIGH2ENTRY(neigh); struct net_device *dev = neigh->dev; struct in_device *in_dev = dev->ip_ptr; DPRINTK("clip_constructor (neigh %p, entry %p)\n",neigh,entry); if (!in_dev) return -EINVAL; neigh->type = inet_addr_type(entry->ip); if (neigh->type != RTN_UNICAST) return -EINVAL; if (in_dev->arp_parms) neigh->parms = in_dev->arp_parms; neigh->ops = &clip_neigh_ops; neigh->output = neigh->nud_state & NUD_VALID ? neigh->ops->connected_output : neigh->ops->output; entry->neigh = neigh; entry->vccs = NULL; entry->expires = jiffies-1; return 0;}static u32 clip_hash(const void *pkey, const struct net_device *dev){ u32 hash_val; hash_val = *(u32*)pkey; hash_val ^= (hash_val>>16); hash_val ^= hash_val>>8; hash_val ^= hash_val>>3; hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK; return hash_val;}struct neigh_table clip_tbl = { NULL, /* next */ AF_INET, /* family */ sizeof(struct neighbour)+sizeof(struct atmarp_entry), /* entry_size */ 4, /* key_len */ clip_hash, clip_constructor, /* constructor */ NULL, /* pconstructor */ NULL, /* pdestructor */ NULL, /* proxy_redo */ "clip_arp_cache", { /* neigh_parms */ NULL, /* next */ NULL, /* neigh_setup */ &clip_tbl, /* tbl */ 0, /* entries */ NULL, /* priv */ NULL, /* sysctl_table */ 30*HZ, /* base_reachable_time */ 1*HZ, /* retrans_time */ 60*HZ, /* gc_staletime */ 30*HZ, /* reachable_time */ 5*HZ, /* delay_probe_time */ 3, /* queue_len */ 3, /* ucast_probes */ 0, /* app_probes */ 3, /* mcast_probes */ 1*HZ, /* anycast_delay */ (8*HZ)/10, /* proxy_delay */ 1*HZ, /* proxy_qlen */ 64 /* locktime */ }, 30*HZ,128,512,1024 /* copied from ARP ... */};/* @@@ copy bh locking from arp.c -- need to bh-enable atm code before *//* * We play with the resolve flag: 0 and 1 have the usual meaning, but -1 means * to allocate the neighbour entry but not to ask atmarpd for resolution. Also, * don't increment the usage count. This is used to create entries in * clip_setentry. */int clip_encap(struct atm_vcc *vcc,int mode){ CLIP_VCC(vcc)->encap = mode; return 0;}static int clip_start_xmit(struct sk_buff *skb,struct net_device *dev){ struct clip_priv *clip_priv = PRIV(dev); struct atmarp_entry *entry;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -