📄 clip.c
字号:
int error; struct clip_vcc *clip_vcc; struct flowi fl = { .nl_u = { .ip4_u = { .daddr = ip, .tos = 1}} }; struct rtable *rt; 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; } pr_debug("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) pr_debug("setentry: add\n"); else { pr_debug("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; pr_debug("registered (net:%s)\n", dev->name); return number;}static int clip_device_event(struct notifier_block *this, unsigned long event, void *arg){ struct net_device *dev = arg; if (dev->nd_net != &init_net) return NOTIFY_DONE; if (event == NETDEV_UNREGISTER) { neigh_ifdown(&clip_tbl, dev); return NOTIFY_DONE; } /* ignore non-CLIP devices */ if (dev->type != ARPHRD_ATM || dev->hard_start_xmit != clip_start_xmit) return NOTIFY_DONE; switch (event) { case NETDEV_UP: pr_debug("clip_device_event NETDEV_UP\n"); to_atmarpd(act_up, PRIV(dev)->number, 0); break; case NETDEV_GOING_DOWN: pr_debug("clip_device_event NETDEV_DOWN\n"); to_atmarpd(act_down, PRIV(dev)->number, 0); break; case NETDEV_CHANGE: case NETDEV_CHANGEMTU: pr_debug("clip_device_event NETDEV_CHANGE*\n"); to_atmarpd(act_change, PRIV(dev)->number, 0); 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 = { .notifier_call = clip_device_event,};static struct notifier_block clip_inet_notifier = { .notifier_call = clip_inet_event,};static void atmarpd_close(struct atm_vcc *vcc){ pr_debug("atmarpd_close\n"); rtnl_lock(); atmarpd = NULL; skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); rtnl_unlock(); pr_debug("(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(atmarpd_dev.lock)};static int atm_init_atmarp(struct atm_vcc *vcc){ rtnl_lock(); if (atmarpd) { rtnl_unlock(); return -EADDRINUSE; } mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ); 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(sk_atm(vcc)); vcc->push = NULL; vcc->pop = NULL; /* crash */ vcc->push_oam = NULL; /* crash */ rtnl_unlock(); 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, (__force __be32)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) || (sk_atm(clip_vcc->vcc)->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 const 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 = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) { rc = -ENOMEM; goto out_kfree; } 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 const 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_no_netlink(&clip_tbl); clip_tbl_hook = &clip_tbl; register_atm_ioctl(&clip_ioctl_ops); register_netdevice_notifier(&clip_dev_notifier); register_inetaddr_notifier(&clip_inet_notifier); setup_timer(&idle_timer, idle_timer_check, 0);#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); unregister_inetaddr_notifier(&clip_inet_notifier); unregister_netdevice_notifier(&clip_dev_notifier); deregister_atm_ioctl(&clip_ioctl_ops); /* First, stop the idle timer, so it stops banging * on the table. */ del_timer_sync(&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_AUTHOR("Werner Almesberger");MODULE_DESCRIPTION("Classical/IP over ATM interface");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -