📄 clip.c
字号:
if (vcc->push != clip_push) { printk(KERN_WARNING "clip_setentry: non-CLIP VCC\n"); return -EBADF; } clip_vcc = CLIP_VCC(vcc); if (!ip) { if (!clip_vcc->entry) { printk(KERN_ERR "hiding hidden ATMARP entry\n"); return 0; } DPRINTK("setentry: remove\n"); unlink_clip_vcc(clip_vcc); return 0; } error = ip_route_output_key(&rt,&fl); if (error) return error; neigh = __neigh_lookup(&clip_tbl,&ip,rt->u.dst.dev,1); ip_rt_put(rt); if (!neigh) return -ENOMEM; entry = NEIGH2ENTRY(neigh); if (entry != clip_vcc->entry) { if (!clip_vcc->entry) DPRINTK("setentry: add\n"); else { DPRINTK("setentry: update\n"); unlink_clip_vcc(clip_vcc); } link_vcc(clip_vcc,entry); } error = neigh_update(neigh, llc_oui, NUD_PERMANENT, NEIGH_UPDATE_F_OVERRIDE|NEIGH_UPDATE_F_ADMIN); neigh_release(neigh); return error;}static void clip_setup(struct net_device *dev){ dev->hard_start_xmit = clip_start_xmit; /* sg_xmit ... */ dev->get_stats = clip_get_stats; dev->type = ARPHRD_ATM; dev->hard_header_len = RFC1483LLC_LEN; dev->mtu = RFC1626_MTU; dev->tx_queue_len = 100; /* "normal" queue (packets) */ /* When using a "real" qdisc, the qdisc determines the queue */ /* length. tx_queue_len is only used for the default case, */ /* without any more elaborate queuing. 100 is a reasonable */ /* compromise between decent burst-tolerance and protection */ /* against memory hogs. */}static int clip_create(int number){ struct net_device *dev; struct clip_priv *clip_priv; int error; if (number != -1) { for (dev = clip_devs; dev; dev = PRIV(dev)->next) if (PRIV(dev)->number == number) return -EEXIST; } else { number = 0; for (dev = clip_devs; dev; dev = PRIV(dev)->next) if (PRIV(dev)->number >= number) number = PRIV(dev)->number+1; } dev = alloc_netdev(sizeof(struct clip_priv), "", clip_setup); if (!dev) return -ENOMEM; clip_priv = PRIV(dev); sprintf(dev->name,"atm%d",number); spin_lock_init(&clip_priv->xoff_lock); clip_priv->number = number; error = register_netdev(dev); if (error) { free_netdev(dev); return error; } clip_priv->next = clip_devs; clip_devs = dev; DPRINTK("registered (net:%s)\n",dev->name); return number;}static int clip_device_event(struct notifier_block *this,unsigned long event, void *dev){ /* ignore non-CLIP devices */ if (((struct net_device *) dev)->type != ARPHRD_ATM || ((struct net_device *) dev)->hard_start_xmit != clip_start_xmit) return NOTIFY_DONE; switch (event) { case NETDEV_UP: DPRINTK("clip_device_event NETDEV_UP\n"); (void) to_atmarpd(act_up,PRIV(dev)->number,0); break; case NETDEV_GOING_DOWN: DPRINTK("clip_device_event NETDEV_DOWN\n"); (void) to_atmarpd(act_down,PRIV(dev)->number,0); break; case NETDEV_CHANGE: case NETDEV_CHANGEMTU: DPRINTK("clip_device_event NETDEV_CHANGE*\n"); (void) to_atmarpd(act_change,PRIV(dev)->number,0); break; case NETDEV_REBOOT: case NETDEV_REGISTER: case NETDEV_DOWN: DPRINTK("clip_device_event %ld\n",event); /* ignore */ break; default: printk(KERN_WARNING "clip_device_event: unknown event " "%ld\n",event); break; } return NOTIFY_DONE;}static int clip_inet_event(struct notifier_block *this,unsigned long event, void *ifa){ struct in_device *in_dev; in_dev = ((struct in_ifaddr *) ifa)->ifa_dev; if (!in_dev || !in_dev->dev) { printk(KERN_WARNING "clip_inet_event: no device\n"); return NOTIFY_DONE; } /* * Transitions are of the down-change-up type, so it's sufficient to * handle the change on up. */ if (event != NETDEV_UP) return NOTIFY_DONE; return clip_device_event(this,NETDEV_CHANGE,in_dev->dev);}static struct notifier_block clip_dev_notifier = { clip_device_event, NULL, 0};static struct notifier_block clip_inet_notifier = { clip_inet_event, NULL, 0};static void atmarpd_close(struct atm_vcc *vcc){ DPRINTK("atmarpd_close\n"); atmarpd = NULL; /* assumed to be atomic */ barrier(); unregister_inetaddr_notifier(&clip_inet_notifier); unregister_netdevice_notifier(&clip_dev_notifier); if (skb_peek(&vcc->sk->sk_receive_queue)) printk(KERN_ERR "atmarpd_close: closing with requests " "pending\n"); skb_queue_purge(&vcc->sk->sk_receive_queue); DPRINTK("(done)\n"); module_put(THIS_MODULE);}static struct atmdev_ops atmarpd_dev_ops = { .close = atmarpd_close};static struct atm_dev atmarpd_dev = { .ops = &atmarpd_dev_ops, .type = "arpd", .number = 999, .lock = SPIN_LOCK_UNLOCKED};static int atm_init_atmarp(struct atm_vcc *vcc){ if (atmarpd) return -EADDRINUSE; if (start_timer) { start_timer = 0; init_timer(&idle_timer); idle_timer.expires = jiffies+CLIP_CHECK_INTERVAL*HZ; idle_timer.function = idle_timer_check; add_timer(&idle_timer); } atmarpd = vcc; set_bit(ATM_VF_META,&vcc->flags); set_bit(ATM_VF_READY,&vcc->flags); /* allow replies and avoid getting closed if signaling dies */ vcc->dev = &atmarpd_dev; vcc_insert_socket(vcc->sk); vcc->push = NULL; vcc->pop = NULL; /* crash */ vcc->push_oam = NULL; /* crash */ if (register_netdevice_notifier(&clip_dev_notifier)) printk(KERN_ERR "register_netdevice_notifier failed\n"); if (register_inetaddr_notifier(&clip_inet_notifier)) printk(KERN_ERR "register_inetaddr_notifier failed\n"); return 0;}static int clip_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ struct atm_vcc *vcc = ATM_SD(sock); int err = 0; switch (cmd) { case SIOCMKCLIP: case ATMARPD_CTRL: case ATMARP_MKIP: case ATMARP_SETENTRY: case ATMARP_ENCAP: if (!capable(CAP_NET_ADMIN)) return -EPERM; break; default: return -ENOIOCTLCMD; } switch (cmd) { case SIOCMKCLIP: err = clip_create(arg); break; case ATMARPD_CTRL: err = atm_init_atmarp(vcc); if (!err) { sock->state = SS_CONNECTED; __module_get(THIS_MODULE); } break; case ATMARP_MKIP: err = clip_mkip(vcc ,arg); break; case ATMARP_SETENTRY: err = clip_setentry(vcc, arg); break; case ATMARP_ENCAP: err = clip_encap(vcc, arg); break; } return err;}static struct atm_ioctl clip_ioctl_ops = { .owner = THIS_MODULE, .ioctl = clip_ioctl,};#ifdef CONFIG_PROC_FSstatic void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr){ static int code[] = { 1,2,10,6,1,0 }; static int e164[] = { 1,8,4,6,1,0 }; if (*addr->sas_addr.pub) { seq_printf(seq, "%s", addr->sas_addr.pub); if (*addr->sas_addr.prv) seq_putc(seq, '+'); } else if (!*addr->sas_addr.prv) { seq_printf(seq, "%s", "(none)"); return; } if (*addr->sas_addr.prv) { unsigned char *prv = addr->sas_addr.prv; int *fields; int i, j; fields = *prv == ATM_AFI_E164 ? e164 : code; for (i = 0; fields[i]; i++) { for (j = fields[i]; j; j--) seq_printf(seq, "%02X", *prv++); if (fields[i+1]) seq_putc(seq, '.'); } }}/* This means the neighbour entry has no attached VCC objects. */#define SEQ_NO_VCC_TOKEN ((void *) 2)static void atmarp_info(struct seq_file *seq, struct net_device *dev, struct atmarp_entry *entry, struct clip_vcc *clip_vcc){ unsigned long exp; char buf[17]; int svc, llc, off; svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || (clip_vcc->vcc->sk->sk_family == AF_ATMSVC)); llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) || clip_vcc->encap); if (clip_vcc == SEQ_NO_VCC_TOKEN) exp = entry->neigh->used; else exp = clip_vcc->last_use; exp = (jiffies - exp) / HZ; seq_printf(seq, "%-6s%-4s%-4s%5ld ", dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp); off = scnprintf(buf, sizeof(buf) - 1, "%d.%d.%d.%d", NIPQUAD(entry->ip)); while (off < 16) buf[off++] = ' '; buf[off] = '\0'; seq_printf(seq, "%s", buf); if (clip_vcc == SEQ_NO_VCC_TOKEN) { if (time_before(jiffies, entry->expires)) seq_printf(seq, "(resolving)\n"); else seq_printf(seq, "(expired, ref %d)\n", atomic_read(&entry->neigh->refcnt)); } else if (!svc) { seq_printf(seq, "%d.%d.%d\n", clip_vcc->vcc->dev->number, clip_vcc->vcc->vpi, clip_vcc->vcc->vci); } else { svc_addr(seq, &clip_vcc->vcc->remote); seq_putc(seq, '\n'); }}struct clip_seq_state { /* This member must be first. */ struct neigh_seq_state ns; /* Local to clip specific iteration. */ struct clip_vcc *vcc;};static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e, struct clip_vcc *curr){ if (!curr) { curr = e->vccs; if (!curr) return SEQ_NO_VCC_TOKEN; return curr; } if (curr == SEQ_NO_VCC_TOKEN) return NULL; curr = curr->next; return curr;}static void *clip_seq_vcc_walk(struct clip_seq_state *state, struct atmarp_entry *e, loff_t *pos){ struct clip_vcc *vcc = state->vcc; vcc = clip_seq_next_vcc(e, vcc); if (vcc && pos != NULL) { while (*pos) { vcc = clip_seq_next_vcc(e, vcc); if (!vcc) break; --(*pos); } } state->vcc = vcc; return vcc;} static void *clip_seq_sub_iter(struct neigh_seq_state *_state, struct neighbour *n, loff_t *pos){ struct clip_seq_state *state = (struct clip_seq_state *) _state; return clip_seq_vcc_walk(state, NEIGH2ENTRY(n), pos);}static void *clip_seq_start(struct seq_file *seq, loff_t *pos){ return neigh_seq_start(seq, pos, &clip_tbl, NEIGH_SEQ_NEIGH_ONLY);}static int clip_seq_show(struct seq_file *seq, void *v){ static char atm_arp_banner[] = "IPitf TypeEncp Idle IP address ATM address\n"; if (v == SEQ_START_TOKEN) { seq_puts(seq, atm_arp_banner); } else { struct clip_seq_state *state = seq->private; struct neighbour *n = v; struct clip_vcc *vcc = state->vcc; atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc); } return 0;}static struct seq_operations arp_seq_ops = { .start = clip_seq_start, .next = neigh_seq_next, .stop = neigh_seq_stop, .show = clip_seq_show,};static int arp_seq_open(struct inode *inode, struct file *file){ struct clip_seq_state *state; struct seq_file *seq; int rc = -EAGAIN; state = kmalloc(sizeof(*state), GFP_KERNEL); if (!state) { rc = -ENOMEM; goto out_kfree; } memset(state, 0, sizeof(*state)); state->ns.neigh_sub_iter = clip_seq_sub_iter; rc = seq_open(file, &arp_seq_ops); if (rc) goto out_kfree; seq = file->private_data; seq->private = state;out: return rc;out_kfree: kfree(state); goto out;}static struct file_operations arp_seq_fops = { .open = arp_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, .owner = THIS_MODULE};#endifstatic int __init atm_clip_init(void){ neigh_table_init(&clip_tbl); clip_tbl_hook = &clip_tbl; register_atm_ioctl(&clip_ioctl_ops);#ifdef CONFIG_PROC_FS{ struct proc_dir_entry *p; p = create_proc_entry("arp", S_IRUGO, atm_proc_root); if (p) p->proc_fops = &arp_seq_fops;}#endif return 0;}static void __exit atm_clip_exit(void){ struct net_device *dev, *next; remove_proc_entry("arp", atm_proc_root); deregister_atm_ioctl(&clip_ioctl_ops); /* First, stop the idle timer, so it stops banging * on the table. */ if (start_timer == 0) del_timer(&idle_timer); /* Next, purge the table, so that the device * unregister loop below does not hang due to * device references remaining in the table. */ neigh_ifdown(&clip_tbl, NULL); dev = clip_devs; while (dev) { next = PRIV(dev)->next; unregister_netdev(dev); free_netdev(dev); dev = next; } /* Now it is safe to fully shutdown whole table. */ neigh_table_clear(&clip_tbl); clip_tbl_hook = NULL;}module_init(atm_clip_init);module_exit(atm_clip_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -