📄 netfilter.c
字号:
/* * Any packet that leaves via this function must come back * through nf_reinject(). */static int nf_queue(struct sk_buff *skb, struct list_head *elem, int pf, unsigned int hook, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *)){ int status; struct nf_info *info;#ifdef CONFIG_BRIDGE_NETFILTER struct net_device *physindev = NULL; struct net_device *physoutdev = NULL;#endif /* QUEUE == DROP if noone is waiting, to be safe. */ read_lock(&queue_handler_lock); if (!queue_handler[pf].outfn) { read_unlock(&queue_handler_lock); kfree_skb(skb); return 1; } info = kmalloc(sizeof(*info), GFP_ATOMIC); if (!info) { if (net_ratelimit()) printk(KERN_ERR "OOM queueing packet %p\n", skb); read_unlock(&queue_handler_lock); kfree_skb(skb); return 1; } *info = (struct nf_info) { (struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn }; /* If it's going away, ignore hook. */ if (!try_module_get(info->elem->owner)) { read_unlock(&queue_handler_lock); kfree(info); return 0; } /* Bump dev refs so they don't vanish while packet is out */ if (indev) dev_hold(indev); if (outdev) dev_hold(outdev);#ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) { physindev = skb->nf_bridge->physindev; if (physindev) dev_hold(physindev); physoutdev = skb->nf_bridge->physoutdev; if (physoutdev) dev_hold(physoutdev); }#endif status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data); read_unlock(&queue_handler_lock); if (status < 0) { /* James M doesn't say fuck enough. */ if (indev) dev_put(indev); if (outdev) dev_put(outdev);#ifdef CONFIG_BRIDGE_NETFILTER if (physindev) dev_put(physindev); if (physoutdev) dev_put(physoutdev);#endif module_put(info->elem->owner); kfree(info); kfree_skb(skb); return 1; } return 1;}int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int hook_thresh){ struct list_head *elem; unsigned int verdict; int ret = 0; /* We may already have this, but read-locks nest anyway */ rcu_read_lock();#ifdef CONFIG_NETFILTER_DEBUG if (skb->nf_debug & (1 << hook)) { printk("nf_hook: hook %i already set.\n", hook); nf_dump_skb(pf, skb); } skb->nf_debug |= (1 << hook);#endif elem = &nf_hooks[pf][hook]; next_hook: verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev, outdev, &elem, okfn, hook_thresh); if (verdict == NF_QUEUE) { NFDEBUG("nf_hook: Verdict = QUEUE.\n"); if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn)) goto next_hook; } switch (verdict) { case NF_ACCEPT: ret = okfn(skb); break; case NF_DROP: kfree_skb(skb); ret = -EPERM; break; } rcu_read_unlock(); return ret;}void nf_reinject(struct sk_buff *skb, struct nf_info *info, unsigned int verdict){ struct list_head *elem = &info->elem->list; struct list_head *i; rcu_read_lock(); /* Release those devices we held, or Alexey will kill me. */ if (info->indev) dev_put(info->indev); if (info->outdev) dev_put(info->outdev);#ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) { if (skb->nf_bridge->physindev) dev_put(skb->nf_bridge->physindev); if (skb->nf_bridge->physoutdev) dev_put(skb->nf_bridge->physoutdev); }#endif /* Drop reference to owner of hook which queued us. */ module_put(info->elem->owner); list_for_each_rcu(i, &nf_hooks[info->pf][info->hook]) { if (i == elem) break; } if (elem == &nf_hooks[info->pf][info->hook]) { /* The module which sent it to userspace is gone. */ NFDEBUG("%s: module disappeared, dropping packet.\n", __FUNCTION__); verdict = NF_DROP; } /* Continue traversal iff userspace said ok... */ if (verdict == NF_REPEAT) { elem = elem->prev; verdict = NF_ACCEPT; } if (verdict == NF_ACCEPT) { next_hook: verdict = nf_iterate(&nf_hooks[info->pf][info->hook], &skb, info->hook, info->indev, info->outdev, &elem, info->okfn, INT_MIN); } switch (verdict) { case NF_ACCEPT: info->okfn(skb); break; case NF_QUEUE: if (!nf_queue(skb, elem, info->pf, info->hook, info->indev, info->outdev, info->okfn)) goto next_hook; break; } rcu_read_unlock(); if (verdict == NF_DROP) kfree_skb(skb); kfree(info); return;}#ifdef CONFIG_INET/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */int ip_route_me_harder(struct sk_buff **pskb){ struct iphdr *iph = (*pskb)->nh.iph; struct rtable *rt; struct flowi fl = {}; struct dst_entry *odst; unsigned int hh_len; /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause * packets with foreign saddr to appear on the NF_IP_LOCAL_OUT hook. */ if (inet_addr_type(iph->saddr) == RTN_LOCAL) { fl.nl_u.ip4_u.daddr = iph->daddr; fl.nl_u.ip4_u.saddr = iph->saddr; fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); fl.oif = (*pskb)->sk ? (*pskb)->sk->sk_bound_dev_if : 0;#ifdef CONFIG_IP_ROUTE_FWMARK fl.nl_u.ip4_u.fwmark = (*pskb)->nfmark;#endif fl.proto = iph->protocol; if (ip_route_output_key(&rt, &fl) != 0) return -1; /* Drop old route. */ dst_release((*pskb)->dst); (*pskb)->dst = &rt->u.dst; } else { /* non-local src, find valid iif to satisfy * rp-filter when calling ip_route_input. */ fl.nl_u.ip4_u.daddr = iph->saddr; if (ip_route_output_key(&rt, &fl) != 0) return -1; odst = (*pskb)->dst; if (ip_route_input(*pskb, iph->daddr, iph->saddr, RT_TOS(iph->tos), rt->u.dst.dev) != 0) { dst_release(&rt->u.dst); return -1; } dst_release(&rt->u.dst); dst_release(odst); } if ((*pskb)->dst->error) return -1; /* Change in oif may mean change in hh_len. */ hh_len = (*pskb)->dst->dev->hard_header_len; if (skb_headroom(*pskb) < hh_len) { struct sk_buff *nskb; nskb = skb_realloc_headroom(*pskb, hh_len); if (!nskb) return -1; if ((*pskb)->sk) skb_set_owner_w(nskb, (*pskb)->sk); kfree_skb(*pskb); *pskb = nskb; } return 0;}int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len){ struct sk_buff *nskb; unsigned int iplen; if (writable_len > (*pskb)->len) return 0; /* Not exclusive use of packet? Must copy. */ if (skb_shared(*pskb) || skb_cloned(*pskb)) goto copy_skb; /* Alexey says IP hdr is always modifiable and linear, so ok. */ if (writable_len <= (*pskb)->nh.iph->ihl*4) return 1; iplen = writable_len - (*pskb)->nh.iph->ihl*4; /* DaveM says protocol headers are also modifiable. */ switch ((*pskb)->nh.iph->protocol) { case IPPROTO_TCP: { struct tcphdr _hdr, *hp; hp = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_hdr), &_hdr); if (hp == NULL) goto copy_skb; if (writable_len <= (*pskb)->nh.iph->ihl*4 + hp->doff*4) goto pull_skb; goto copy_skb; } case IPPROTO_UDP: if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) goto pull_skb; goto copy_skb; case IPPROTO_ICMP: if (writable_len <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) goto pull_skb; goto copy_skb; /* Insert other cases here as desired */ }copy_skb: nskb = skb_copy(*pskb, GFP_ATOMIC); if (!nskb) return 0; BUG_ON(skb_is_nonlinear(nskb)); /* Rest of kernel will get very unhappy if we pass it a suddenly-orphaned skbuff */ if ((*pskb)->sk) skb_set_owner_w(nskb, (*pskb)->sk); kfree_skb(*pskb); *pskb = nskb; return 1;pull_skb: return pskb_may_pull(*pskb, writable_len);}EXPORT_SYMBOL(skb_ip_make_writable);#endif /*CONFIG_INET*//* Internal logging interface, which relies on the real LOG target modules */#define NF_LOG_PREFIXLEN 128static nf_logfn *nf_logging[NPROTO]; /* = NULL */static int reported = 0;static spinlock_t nf_log_lock = SPIN_LOCK_UNLOCKED;int nf_log_register(int pf, nf_logfn *logfn){ int ret = -EBUSY; /* Any setup of logging members must be done before * substituting pointer. */ smp_wmb(); spin_lock(&nf_log_lock); if (!nf_logging[pf]) { nf_logging[pf] = logfn; ret = 0; } spin_unlock(&nf_log_lock); return ret;} void nf_log_unregister(int pf, nf_logfn *logfn){ spin_lock(&nf_log_lock); if (nf_logging[pf] == logfn) nf_logging[pf] = NULL; spin_unlock(&nf_log_lock); /* Give time to concurrent readers. */ synchronize_net();} void nf_log_packet(int pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const char *fmt, ...){ va_list args; char prefix[NF_LOG_PREFIXLEN]; nf_logfn *logfn; rcu_read_lock(); logfn = rcu_dereference(nf_logging[pf]); if (logfn) { va_start(args, fmt); vsnprintf(prefix, sizeof(prefix), fmt, args); va_end(args); /* We must read logging before nf_logfn[pf] */ logfn(hooknum, skb, in, out, prefix); } else if (!reported) { printk(KERN_WARNING "nf_log_packet: can\'t log yet, " "no backend logging module loaded in!\n"); reported++; } rcu_read_unlock();}EXPORT_SYMBOL(nf_log_register);EXPORT_SYMBOL(nf_log_unregister);EXPORT_SYMBOL(nf_log_packet);/* This does not belong here, but ipt_REJECT needs it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated with it. */void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);void __init netfilter_init(void){ int i, h; for (i = 0; i < NPROTO; i++) { for (h = 0; h < NF_MAX_HOOKS; h++) INIT_LIST_HEAD(&nf_hooks[i][h]); }}EXPORT_SYMBOL(ip_ct_attach);EXPORT_SYMBOL(ip_route_me_harder);EXPORT_SYMBOL(nf_getsockopt);EXPORT_SYMBOL(nf_hook_slow);EXPORT_SYMBOL(nf_hooks);EXPORT_SYMBOL(nf_register_hook);EXPORT_SYMBOL(nf_register_queue_handler);EXPORT_SYMBOL(nf_register_sockopt);EXPORT_SYMBOL(nf_reinject);EXPORT_SYMBOL(nf_setsockopt);EXPORT_SYMBOL(nf_unregister_hook);EXPORT_SYMBOL(nf_unregister_queue_handler);EXPORT_SYMBOL(nf_unregister_sockopt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -