📄 devinet.c
字号:
ASSERT_RTNL(); if (!in_dev) { if (event == NETDEV_REGISTER) { in_dev = inetdev_init(dev); if (!in_dev) return notifier_from_errno(-ENOMEM); if (dev->flags & IFF_LOOPBACK) { IN_DEV_CONF_SET(in_dev, NOXFRM, 1); IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); } } goto out; } switch (event) { case NETDEV_REGISTER: printk(KERN_DEBUG "inetdev_event: bug\n"); dev->ip_ptr = NULL; break; case NETDEV_UP: if (dev->mtu < 68) break; if (dev->flags & IFF_LOOPBACK) { struct in_ifaddr *ifa; if ((ifa = inet_alloc_ifa()) != NULL) { ifa->ifa_local = ifa->ifa_address = htonl(INADDR_LOOPBACK); ifa->ifa_prefixlen = 8; ifa->ifa_mask = inet_make_mask(8); in_dev_hold(in_dev); ifa->ifa_dev = in_dev; ifa->ifa_scope = RT_SCOPE_HOST; memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); inet_insert_ifa(ifa); } } ip_mc_up(in_dev); break; case NETDEV_DOWN: ip_mc_down(in_dev); break; case NETDEV_CHANGEMTU: if (dev->mtu >= 68) break; /* MTU falled under 68, disable IP */ case NETDEV_UNREGISTER: inetdev_destroy(in_dev); break; case NETDEV_CHANGENAME: /* Do not notify about label change, this event is * not interesting to applications using netlink. */ inetdev_changename(dev, in_dev);#ifdef CONFIG_SYSCTL devinet_sysctl_unregister(&in_dev->cnf); neigh_sysctl_unregister(in_dev->arp_parms); neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4", NULL, NULL); devinet_sysctl_register(in_dev, &in_dev->cnf);#endif break; }out: return NOTIFY_DONE;}static struct notifier_block ip_netdev_notifier = { .notifier_call =inetdev_event,};static inline size_t inet_nlmsg_size(void){ return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + nla_total_size(4) /* IFA_ADDRESS */ + nla_total_size(4) /* IFA_LOCAL */ + nla_total_size(4) /* IFA_BROADCAST */ + nla_total_size(4) /* IFA_ANYCAST */ + nla_total_size(IFNAMSIZ); /* IFA_LABEL */}static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, u32 pid, u32 seq, int event, unsigned int flags){ struct ifaddrmsg *ifm; struct nlmsghdr *nlh; nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); if (nlh == NULL) return -EMSGSIZE; ifm = nlmsg_data(nlh); ifm->ifa_family = AF_INET; ifm->ifa_prefixlen = ifa->ifa_prefixlen; ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; ifm->ifa_scope = ifa->ifa_scope; ifm->ifa_index = ifa->ifa_dev->dev->ifindex; if (ifa->ifa_address) NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address); if (ifa->ifa_local) NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local); if (ifa->ifa_broadcast) NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast); if (ifa->ifa_anycast) NLA_PUT_BE32(skb, IFA_ANYCAST, ifa->ifa_anycast); if (ifa->ifa_label[0]) NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label); return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb){ int idx, ip_idx; struct net_device *dev; struct in_device *in_dev; struct in_ifaddr *ifa; int s_ip_idx, s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; idx = 0; for_each_netdev(&init_net, dev) { if (idx < s_idx) goto cont; if (idx > s_idx) s_ip_idx = 0; if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) goto cont; for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; ifa = ifa->ifa_next, ip_idx++) { if (ip_idx < s_ip_idx) continue; if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR, NLM_F_MULTI) <= 0) goto done; }cont: idx++; }done: cb->args[0] = idx; cb->args[1] = ip_idx; return skb->len;}static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh, u32 pid){ struct sk_buff *skb; u32 seq = nlh ? nlh->nlmsg_seq : 0; int err = -ENOBUFS; skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); if (skb == NULL) goto errout; err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0); if (err < 0) { /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } err = rtnl_notify(skb, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);errout: if (err < 0) rtnl_set_sk_err(RTNLGRP_IPV4_IFADDR, err);}#ifdef CONFIG_SYSCTLstatic void devinet_copy_dflt_conf(int i){ struct net_device *dev; read_lock(&dev_base_lock); for_each_netdev(&init_net, dev) { struct in_device *in_dev; rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev && !test_bit(i, in_dev->cnf.state)) in_dev->cnf.data[i] = ipv4_devconf_dflt.data[i]; rcu_read_unlock(); } read_unlock(&dev_base_lock);}static int devinet_conf_proc(ctl_table *ctl, int write, struct file* filp, void __user *buffer, size_t *lenp, loff_t *ppos){ int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); if (write) { struct ipv4_devconf *cnf = ctl->extra1; int i = (int *)ctl->data - cnf->data; set_bit(i, cnf->state); if (cnf == &ipv4_devconf_dflt) devinet_copy_dflt_conf(i); } return ret;}static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen){ struct ipv4_devconf *cnf; int *valp = table->data; int new; int i; if (!newval || !newlen) return 0; if (newlen != sizeof(int)) return -EINVAL; if (get_user(new, (int __user *)newval)) return -EFAULT; if (new == *valp) return 0; if (oldval && oldlenp) { size_t len; if (get_user(len, oldlenp)) return -EFAULT; if (len) { if (len > table->maxlen) len = table->maxlen; if (copy_to_user(oldval, valp, len)) return -EFAULT; if (put_user(len, oldlenp)) return -EFAULT; } } *valp = new; cnf = table->extra1; i = (int *)table->data - cnf->data; set_bit(i, cnf->state); if (cnf == &ipv4_devconf_dflt) devinet_copy_dflt_conf(i); return 1;}void inet_forward_change(void){ struct net_device *dev; int on = IPV4_DEVCONF_ALL(FORWARDING); IPV4_DEVCONF_ALL(ACCEPT_REDIRECTS) = !on; IPV4_DEVCONF_DFLT(FORWARDING) = on; read_lock(&dev_base_lock); for_each_netdev(&init_net, dev) { struct in_device *in_dev; rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) IN_DEV_CONF_SET(in_dev, FORWARDING, on); rcu_read_unlock(); } read_unlock(&dev_base_lock); rt_cache_flush(0);}static int devinet_sysctl_forward(ctl_table *ctl, int write, struct file* filp, void __user *buffer, size_t *lenp, loff_t *ppos){ int *valp = ctl->data; int val = *valp; int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); if (write && *valp != val) { if (valp == &IPV4_DEVCONF_ALL(FORWARDING)) inet_forward_change(); else if (valp != &IPV4_DEVCONF_DFLT(FORWARDING)) rt_cache_flush(0); } return ret;}int ipv4_doint_and_flush(ctl_table *ctl, int write, struct file* filp, void __user *buffer, size_t *lenp, loff_t *ppos){ int *valp = ctl->data; int val = *valp; int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); if (write && *valp != val) rt_cache_flush(0); return ret;}int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen){ int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp, newval, newlen); if (ret == 1) rt_cache_flush(0); return ret;}#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc, sysctl) \ { \ .ctl_name = NET_IPV4_CONF_ ## attr, \ .procname = name, \ .data = ipv4_devconf.data + \ NET_IPV4_CONF_ ## attr - 1, \ .maxlen = sizeof(int), \ .mode = mval, \ .proc_handler = proc, \ .strategy = sysctl, \ .extra1 = &ipv4_devconf, \ }#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc, \ devinet_conf_sysctl)#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc, \ devinet_conf_sysctl)#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc, sysctl) \ DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc, sysctl)#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush, \ ipv4_doint_and_flush_strategy)static struct devinet_sysctl_table { struct ctl_table_header *sysctl_header; ctl_table devinet_vars[__NET_IPV4_CONF_MAX]; ctl_table devinet_dev[2]; ctl_table devinet_conf_dir[2]; ctl_table devinet_proto_dir[2]; ctl_table devinet_root_dir[2];} devinet_sysctl = { .devinet_vars = { DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", devinet_sysctl_forward, devinet_conf_sysctl), DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, "accept_source_route"), DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, "promote_secondaries"), }, .devinet_dev = { { .ctl_name = NET_PROTO_CONF_ALL, .procname = "all", .mode = 0555, .child = devinet_sysctl.devinet_vars, }, }, .devinet_conf_dir = { { .ctl_name = NET_IPV4_CONF, .procname = "conf", .mode = 0555, .child = devinet_sysctl.devinet_dev, }, }, .devinet_proto_dir = { { .ctl_name = NET_IPV4, .procname = "ipv4", .mode = 0555, .child = devinet_sysctl.devinet_conf_dir, }, }, .devinet_root_dir = { { .ctl_name = CTL_NET, .procname = "net", .mode = 0555, .child = devinet_sysctl.devinet_proto_dir, }, },};static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p){ int i; struct net_device *dev = in_dev ? in_dev->dev : NULL; struct devinet_sysctl_table *t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); char *dev_name = NULL; if (!t) return; for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; t->devinet_vars[i].extra1 = p; } if (dev) { dev_name = dev->name; t->devinet_dev[0].ctl_name = dev->ifindex; } else { dev_name = "default"; t->devinet_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT; } /* * Make a copy of dev_name, because '.procname' is regarded as const * by sysctl and we wouldn't want anyone to change it under our feet * (see SIOCSIFNAME). */ dev_name = kstrdup(dev_name, GFP_KERNEL); if (!dev_name) goto free; t->devinet_dev[0].procname = dev_name; t->devinet_dev[0].child = t->devinet_vars; t->devinet_conf_dir[0].child = t->devinet_dev; t->devinet_proto_dir[0].child = t->devinet_conf_dir; t->devinet_root_dir[0].child = t->devinet_proto_dir; t->sysctl_header = register_sysctl_table(t->devinet_root_dir); if (!t->sysctl_header) goto free_procname; p->sysctl = t; return; /* error path */ free_procname: kfree(dev_name); free: kfree(t); return;}static void devinet_sysctl_unregister(struct ipv4_devconf *p){ if (p->sysctl) { struct devinet_sysctl_table *t = p->sysctl; p->sysctl = NULL; unregister_sysctl_table(t->sysctl_header); kfree(t->devinet_dev[0].procname); kfree(t); }}#endifvoid __init devinet_init(void){ register_gifconf(PF_INET, inet_gifconf); register_netdevice_notifier(&ip_netdev_notifier); rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);#ifdef CONFIG_SYSCTL devinet_sysctl.sysctl_header = register_sysctl_table(devinet_sysctl.devinet_root_dir); devinet_sysctl_register(NULL, &ipv4_devconf_dflt);#endif}EXPORT_SYMBOL(in_dev_finish_destroy);EXPORT_SYMBOL(inet_select_addr);EXPORT_SYMBOL(inetdev_by_index);EXPORT_SYMBOL(register_inetaddr_notifier);EXPORT_SYMBOL(unregister_inetaddr_notifier);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -