Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257// SPDX-License-Identifier: GPL-2.0-only/* * This is a module which is used for queueing packets and communicating with * userspace via nfnetlink. * * (C) 2005 by Harald Welte <laforge@netfilter.org> * (C) 2007 by Patrick McHardy <kaber@trash.net> * * Based on the old ipv4-only ip_queue.c: * (C) 2000-2002 James Morris <jmorris@intercode.com.au> * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/netfilter.h>#include <linux/proc_fs.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_ipv6.h>#include <linux/netfilter_bridge.h>#include <linux/netfilter/nfnetlink.h>#include <linux/netfilter/nfnetlink_queue.h>#include <linux/netfilter/nf_conntrack_common.h>#include <linux/icmp.h>#include <linux/icmpv6.h>#include <linux/ip.h>#include <linux/list.h>#include <linux/sctp.h>#include <linux/cgroup-defs.h>#include <linux/rhashtable.h>#include <linux/jhash.h>#include <linux/tcp.h>#include <linux/udp.h>#include <net/gre.h>#include <net/gso.h>#include <net/ip.h>#include <net/sock.h>#include <net/tcp_states.h>#include <net/netfilter/nf_queue.h>#include <net/netns/generic.h>
#include <linux/atomic.h>
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)#include "../bridge/br_private.h"#endif
#if IS_ENABLED(CONFIG_NF_CONNTRACK)#include <net/netfilter/nf_conntrack.h>#endif
#define NFQNL_QMAX_DEFAULT 1024#define NFQNL_HASH_MIN 8#define NFQNL_HASH_MAX 32768
/* We're using struct nlattr which has 16bit nla_len. Note that nla_len * includes the header length. Thus, the maximum packet length that we * support is 65531 bytes. We send truncated packets if the specified length * is larger than that. Userspace can check for presence of NFQA_CAP_LEN * attribute to detect truncation. */#define NFQNL_MAX_COPY_RANGE (0xffff - NLA_HDRLEN)
struct nfqnl_instance { struct hlist_node hlist; /* global list of queues */ struct rhashtable nfqnl_packet_map; struct rcu_work rwork;
u32 peer_portid; unsigned int queue_maxlen; unsigned int copy_range; unsigned int queue_dropped; unsigned int queue_user_dropped;
u_int16_t queue_num; /* number of this queue */ u_int8_t copy_mode; u_int32_t flags; /* Set using NFQA_CFG_FLAGS *//* * Following fields are dirtied for each queued packet, * keep them in same cache line if possible. */ spinlock_t lock ____cacheline_aligned_in_smp; unsigned int queue_total; unsigned int id_sequence; /* 'sequence' of pkt ids */ struct list_head queue_list; /* packets in queue */};
typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long);
static struct workqueue_struct *nfq_cleanup_wq __read_mostly;static unsigned int nfnl_queue_net_id __read_mostly;
#define INSTANCE_BUCKETS 16struct nfnl_queue_net { spinlock_t instances_lock; struct hlist_head instance_table[INSTANCE_BUCKETS];};
static struct nfnl_queue_net *nfnl_queue_pernet(struct net *net){ return net_generic(net, nfnl_queue_net_id);}
static inline u_int8_t instance_hashfn(u_int16_t queue_num){ return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS;}
static const struct rhashtable_params nfqnl_rhashtable_params = { .head_offset = offsetof(struct nf_queue_entry, hash_node), .key_offset = offsetof(struct nf_queue_entry, id), .key_len = sizeof(u32), .automatic_shrinking = true, .min_size = NFQNL_HASH_MIN, .max_size = NFQNL_HASH_MAX,};
static struct nfqnl_instance *instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num){ struct hlist_head *head; struct nfqnl_instance *inst;
head = &q->instance_table[instance_hashfn(queue_num)]; hlist_for_each_entry_rcu(inst, head, hlist) { if (inst->queue_num == queue_num) return inst; } return NULL;}
static struct nfqnl_instance *instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid){ struct nfqnl_instance *inst; unsigned int h; int err;
inst = kzalloc_obj(*inst, GFP_KERNEL_ACCOUNT); if (!inst) return ERR_PTR(-ENOMEM);
inst->queue_num = queue_num; inst->peer_portid = portid; inst->queue_maxlen = NFQNL_QMAX_DEFAULT; inst->copy_range = NFQNL_MAX_COPY_RANGE; inst->copy_mode = NFQNL_COPY_NONE; spin_lock_init(&inst->lock); INIT_LIST_HEAD(&inst->queue_list);
err = rhashtable_init(&inst->nfqnl_packet_map, &nfqnl_rhashtable_params); if (err < 0) goto out_free;
spin_lock(&q->instances_lock); if (instance_lookup(q, queue_num)) { err = -EEXIST; goto out_unlock; }
if (!try_module_get(THIS_MODULE)) { err = -EAGAIN; goto out_unlock; }
h = instance_hashfn(queue_num); hlist_add_head_rcu(&inst->hlist, &q->instance_table[h]);
spin_unlock(&q->instances_lock);
return inst;
out_unlock: spin_unlock(&q->instances_lock); rhashtable_destroy(&inst->nfqnl_packet_map);out_free: kfree(inst); return ERR_PTR(err);}
static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data);
static void instance_destroy_work(struct work_struct *work){ struct nfqnl_instance *inst;
inst = container_of(to_rcu_work(work), struct nfqnl_instance, rwork); rcu_read_lock(); nfqnl_flush(inst, NULL, 0); rcu_read_unlock();
rhashtable_destroy(&inst->nfqnl_packet_map);
kfree(inst); module_put(THIS_MODULE);}
static void__instance_destroy(struct nfqnl_instance *inst){ hlist_del_rcu(&inst->hlist);
INIT_RCU_WORK(&inst->rwork, instance_destroy_work); queue_rcu_work(nfq_cleanup_wq, &inst->rwork);}
static voidinstance_destroy(struct nfnl_queue_net *q, struct nfqnl_instance *inst){ spin_lock(&q->instances_lock); __instance_destroy(inst); spin_unlock(&q->instances_lock);}
static int__enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry){ int err;
err = rhashtable_insert_fast(&queue->nfqnl_packet_map, &entry->hash_node, nfqnl_rhashtable_params); if (unlikely(err)) return err;
list_add_tail(&entry->list, &queue->queue_list); queue->queue_total++;
return 0;}
static void__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry){ rhashtable_remove_fast(&queue->nfqnl_packet_map, &entry->hash_node, nfqnl_rhashtable_params); list_del(&entry->list); queue->queue_total--;}
static struct nf_queue_entry *find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id){ struct nf_queue_entry *entry;
spin_lock_bh(&queue->lock); entry = rhashtable_lookup_fast(&queue->nfqnl_packet_map, &id, nfqnl_rhashtable_params);
if (entry) __dequeue_entry(queue, entry);
spin_unlock_bh(&queue->lock);
return entry;}
static unsigned int nf_iterate(struct sk_buff *skb, struct nf_hook_state *state, const struct nf_hook_entries *hooks, unsigned int *index){ const struct nf_hook_entry *hook; unsigned int verdict, i = *index;
while (i < hooks->num_hook_entries) { hook = &hooks->hooks[i];repeat: verdict = nf_hook_entry_hookfn(hook, skb, state); if (verdict != NF_ACCEPT) { *index = i; if (verdict != NF_REPEAT) return verdict; goto repeat; } i++; }
*index = i; return NF_ACCEPT;}
static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf, u8 hooknum){ switch (pf) {#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE case NFPROTO_BRIDGE: return rcu_dereference(net->nf.hooks_bridge[hooknum]);#endif case NFPROTO_IPV4: return rcu_dereference(net->nf.hooks_ipv4[hooknum]); case NFPROTO_IPV6: return rcu_dereference(net->nf.hooks_ipv6[hooknum]); default: WARN_ON_ONCE(1); return NULL; }
return NULL;}
static int nf_ip_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry){#ifdef CONFIG_INET const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct iphdr *iph = ip_hdr(skb);
if (!(iph->tos == rt_info->tos && skb->mark == rt_info->mark && iph->daddr == rt_info->daddr && iph->saddr == rt_info->saddr)) return ip_route_me_harder(entry->state.net, entry->state.sk, skb, RTN_UNSPEC); }#endif return 0;}
static int nf_ip6_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry){ struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb);
if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || skb->mark != rt_info->mark) return nf_ip6_route_me_harder(entry->state.net, entry->state.sk, skb); } return 0;}
static int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry){ int ret = 0;
switch (entry->state.pf) { case AF_INET: ret = nf_ip_reroute(skb, entry); break; case AF_INET6: ret = nf_ip6_reroute(skb, entry); break; } return ret;}
/* caller must hold rcu read-side lock */static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict){ const struct nf_hook_entry *hook_entry; const struct nf_hook_entries *hooks; struct sk_buff *skb = entry->skb; const struct net *net; unsigned int i; int err; u8 pf;
net = entry->state.net; pf = entry->state.pf;
hooks = nf_hook_entries_head(net, pf, entry->state.hook);
i = entry->hook_index; if (!hooks || i >= hooks->num_hook_entries) { kfree_skb_reason(skb, SKB_DROP_REASON_NETFILTER_DROP); nf_queue_entry_free(entry); return; }
hook_entry = &hooks->hooks[i];
/* Continue traversal iff userspace said ok... */ if (verdict == NF_REPEAT) verdict = nf_hook_entry_hookfn(hook_entry, skb, &entry->state);
if (verdict == NF_ACCEPT) { if (nf_reroute(skb, entry) < 0) verdict = NF_DROP; }
if (verdict == NF_ACCEPT) {next_hook: ++i; verdict = nf_iterate(skb, &entry->state, hooks, &i); }
switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: case NF_STOP: local_bh_disable(); entry->state.okfn(entry->state.net, entry->state.sk, skb); local_bh_enable(); break; case NF_QUEUE: err = nf_queue(skb, &entry->state, i, verdict); if (err == 1) goto next_hook; break; case NF_STOLEN: break; default: kfree_skb(skb); }
nf_queue_entry_free(entry);}
/* return true if the entry has an unconfirmed conntrack attached that isn't owned by us * exclusively. */static bool nf_ct_drop_unconfirmed(const struct nf_queue_entry *entry, bool *is_unconfirmed){#if IS_ENABLED(CONFIG_NF_CONNTRACK) struct nf_conn *ct = (void *)skb_nfct(entry->skb);
if (!ct || nf_ct_is_confirmed(ct)) return false;
if (is_unconfirmed) *is_unconfirmed = true;
/* in some cases skb_clone() can occur after initial conntrack * pickup, but conntrack assumes exclusive skb->_nfct ownership for * unconfirmed entries. * * This happens for br_netfilter and with ip multicast routing. * This can't be solved with serialization here because one clone * could have been queued for local delivery or could be transmitted * in parallel on another CPU. */ return refcount_read(&ct->ct_general.use) > 1;#endif return false;}
static bool nf_bridge_port_valid(const struct net_device *dev){ if (!dev) return true;
return netif_is_bridge_port(dev);}
/* queued skbs leave rcu protection. We bump device refcount so that * the device cannot go away. However, while packet was out the port * could have been removed from the bridge. * * Ensure in+outdev are still part of a bridge at reinject time. * * The device rx_handler_data could even be pointing at data that is * not a net_bridge_port structure. */static bool nf_bridge_ports_valid(const struct nf_queue_entry *entry){#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (!nf_bridge_port_valid(entry->physin) || !nf_bridge_port_valid(entry->physout)) return false;#endif if (entry->state.pf != PF_BRIDGE) return true;
if (!nf_bridge_port_valid(entry->state.in) || !nf_bridge_port_valid(entry->state.out)) return false;
return true;}
static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict){ const struct nf_ct_hook *ct_hook;
if (!nf_bridge_ports_valid(entry)) verdict = NF_DROP;
if (verdict == NF_ACCEPT || verdict == NF_REPEAT || verdict == NF_STOP) { unsigned int ct_verdict = verdict;
rcu_read_lock(); ct_hook = rcu_dereference(nf_ct_hook); if (ct_hook) ct_verdict = ct_hook->update(entry->state.net, entry->skb); rcu_read_unlock();
switch (ct_verdict & NF_VERDICT_MASK) { case NF_ACCEPT: /* follow userspace verdict, could be REPEAT */ break; case NF_STOLEN: nf_queue_entry_free(entry); return; default: verdict = ct_verdict & NF_VERDICT_MASK; break; } }
if (verdict != NF_DROP && entry->nf_ct_is_unconfirmed) { /* If first queued segment was already reinjected then * there is a good chance the ct entry is now confirmed. * * Handle the rare cases: * - out-of-order verdict * - threaded userspace reinjecting in parallel * - first segment was dropped * * In all of those cases we can't handle this packet * because we can't be sure that another CPU won't modify * nf_conn->ext in parallel which isn't allowed. */ if (nf_ct_drop_unconfirmed(entry, NULL)) verdict = NF_DROP; }
nf_reinject(entry, verdict);}
static voidnfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data){ struct nf_queue_entry *entry, *next;
spin_lock_bh(&queue->lock); list_for_each_entry_safe(entry, next, &queue->queue_list, list) { if (!cmpfn || cmpfn(entry, data)) { __dequeue_entry(queue, entry); nfqnl_reinject(entry, NF_DROP); } } spin_unlock_bh(&queue->lock);}
static intnfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, bool csum_verify){ __u32 flags = 0;
if (packet->ip_summed == CHECKSUM_PARTIAL) flags = NFQA_SKB_CSUMNOTREADY; else if (csum_verify) flags = NFQA_SKB_CSUM_NOTVERIFIED;
if (skb_is_gso(packet)) flags |= NFQA_SKB_GSO;
return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0;}
static int nfqnl_put_sk_uidgid(struct sk_buff *skb, struct sock *sk){ const struct socket *sock; const struct file *file; const struct cred *cred;
if (!sk_fullsock(sk)) return 0;
/* The sk pointer remains valid as long as the skb is. * The sk_socket and file pointer may become NULL * if the socket is closed. * Both structures (including file->cred) are RCU freed * which means they can be accessed within a RCU read section. */ sock = READ_ONCE(sk->sk_socket); file = sock ? READ_ONCE(sock->file) : NULL; if (file) { cred = file->f_cred; if (nla_put_be32(skb, NFQA_UID, htonl(from_kuid_munged(&init_user_ns, cred->fsuid)))) goto nla_put_failure; if (nla_put_be32(skb, NFQA_GID, htonl(from_kgid_munged(&init_user_ns, cred->fsgid)))) goto nla_put_failure; } return 0;
nla_put_failure: return -1;}
static int nfqnl_put_sk_classid(struct sk_buff *skb, struct sock *sk){#if IS_ENABLED(CONFIG_CGROUP_NET_CLASSID) if (sk && sk_fullsock(sk)) { u32 classid = sock_cgroup_classid(&sk->sk_cgrp_data);
if (classid && nla_put_be32(skb, NFQA_CGROUP_CLASSID, htonl(classid))) return -1; }#endif return 0;}
static int nfqnl_get_sk_secctx(struct sk_buff *skb, struct lsm_context *ctx){ int seclen = 0;#if IS_ENABLED(CONFIG_NETWORK_SECMARK) if (skb->secmark) seclen = security_secid_to_secctx(skb->secmark, ctx);#endif return seclen;}
static u32 nfqnl_get_bridge_size(struct nf_queue_entry *entry){ struct sk_buff *entskb = entry->skb; u32 nlalen = 0; u32 mac_len;
if (entry->state.pf != PF_BRIDGE || !skb_mac_header_was_set(entskb)) return 0;
if (skb_vlan_tag_present(entskb)) nlalen += nla_total_size(nla_total_size(sizeof(__be16)) + nla_total_size(sizeof(__be16)));
mac_len = skb_mac_header_len(entskb); if (mac_len > 0) nlalen += nla_total_size(mac_len);
return nlalen;}
static int nfqnl_put_bridge(struct nf_queue_entry *entry, struct sk_buff *skb){ struct sk_buff *entskb = entry->skb; u32 mac_len;
if (entry->state.pf != PF_BRIDGE || !skb_mac_header_was_set(entskb)) return 0;
if (skb_vlan_tag_present(entskb)) { struct nlattr *nest;
nest = nla_nest_start(skb, NFQA_VLAN); if (!nest) goto nla_put_failure;
if (nla_put_be16(skb, NFQA_VLAN_TCI, htons(entskb->vlan_tci)) || nla_put_be16(skb, NFQA_VLAN_PROTO, entskb->vlan_proto)) goto nla_put_failure;
nla_nest_end(skb, nest); }
mac_len = skb_mac_header_len(entskb); if (mac_len > 0 && nla_put(skb, NFQA_L2HDR, mac_len, skb_mac_header(entskb))) goto nla_put_failure;
return 0;
nla_put_failure: return -1;}
static int nf_queue_checksum_help(struct sk_buff *entskb){ if (entskb->protocol == htons(ETH_P_IP)) { if (!pskb_network_may_pull(entskb, sizeof(struct iphdr)) || ip_check_csum_start(entskb)) return -EINVAL; }
if (skb_csum_is_sctp(entskb)) return skb_crc32c_csum_help(entskb);
return skb_checksum_help(entskb);}
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, const struct net_device *dev){ const struct net_device *upper;
if (dev && !netif_is_bridge_port(dev)) return 0;
upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev); if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex))) return -EMSGSIZE;
return 0;}#endif
static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb, unsigned int copy_range){ unsigned int data_len = entskb->len;
if (!skb_frags_readable(entskb)) data_len = skb_headlen(entskb);
return min(data_len, copy_range);}
static struct sk_buff *nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, __be32 **packet_id_ptr){ size_t size; size_t data_len = 0, cap_len = 0; unsigned int hlen = 0; struct sk_buff *skb; struct nlattr *nla; struct nfqnl_msg_packet_hdr *pmsg; struct nlmsghdr *nlh; struct sk_buff *entskb = entry->skb; struct net_device *indev; struct net_device *outdev; struct nf_conn *ct = NULL; enum ip_conntrack_info ctinfo = 0; const struct nfnl_ct_hook *nfnl_ct; bool csum_verify; struct lsm_context ctx = { NULL, 0, 0 }; int seclen = 0; ktime_t tstamp;
size = nlmsg_total_size(sizeof(struct nfgenmsg)) + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + nla_total_size(sizeof(u_int32_t)) /* ifindex */#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + nla_total_size(sizeof(u_int32_t)) /* ifindex */#endif + nla_total_size(sizeof(u_int32_t)) /* mark */ + nla_total_size(sizeof(u_int32_t)) /* priority */ + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) + nla_total_size(sizeof(u_int32_t)) /* skbinfo */#if IS_ENABLED(CONFIG_CGROUP_NET_CLASSID) + nla_total_size(sizeof(u_int32_t)) /* classid */#endif + nla_total_size(sizeof(u_int32_t)); /* cap_len */
tstamp = skb_tstamp_cond(entskb, false); if (tstamp) size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp));
size += nfqnl_get_bridge_size(entry);
if (entry->state.hook <= NF_INET_FORWARD || (entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) csum_verify = !skb_csum_unnecessary(entskb); else csum_verify = false;
outdev = entry->state.out;
switch ((enum nfqnl_config_mode)READ_ONCE(queue->copy_mode)) { case NFQNL_COPY_META: case NFQNL_COPY_NONE: break;
case NFQNL_COPY_PACKET: if (!(queue->flags & NFQA_CFG_F_GSO) && entskb->ip_summed == CHECKSUM_PARTIAL && nf_queue_checksum_help(entskb)) return NULL;
data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range)); hlen = skb_zerocopy_headlen(entskb); hlen = min_t(unsigned int, hlen, data_len); size += sizeof(struct nlattr) + hlen; cap_len = entskb->len; break; }
nfnl_ct = rcu_dereference(nfnl_ct_hook);
#if IS_ENABLED(CONFIG_NF_CONNTRACK) if (queue->flags & NFQA_CFG_F_CONNTRACK) { if (nfnl_ct != NULL) { ct = nf_ct_get(entskb, &ctinfo); if (ct != NULL) size += nfnl_ct->build_size(ct); } }#endif
if (queue->flags & NFQA_CFG_F_UID_GID) { size += (nla_total_size(sizeof(u_int32_t)) /* uid */ + nla_total_size(sizeof(u_int32_t))); /* gid */ }
if ((queue->flags & NFQA_CFG_F_SECCTX) && entskb->sk) { seclen = nfqnl_get_sk_secctx(entskb, &ctx); if (seclen < 0) return NULL; if (seclen) size += nla_total_size(seclen); }
skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { skb_tx_error(entskb); goto nlmsg_failure; }
nlh = nfnl_msg_put(skb, 0, 0, nfnl_msg_type(NFNL_SUBSYS_QUEUE, NFQNL_MSG_PACKET), 0, entry->state.pf, NFNETLINK_V0, htons(queue->queue_num)); if (!nlh) { skb_tx_error(entskb); kfree_skb(skb); goto nlmsg_failure; }
nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); pmsg = nla_data(nla); pmsg->hw_protocol = entskb->protocol; pmsg->hook = entry->state.hook; *packet_id_ptr = &pmsg->packet_id;
indev = entry->state.in; if (indev) {#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) goto nla_put_failure;#else if (entry->state.pf == PF_BRIDGE) { /* Case 1: indev is physical input device, we need to * look for bridge group (when called from * netfilter_bridge) */ if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, htonl(indev->ifindex)) || nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_INDEV, indev)) goto nla_put_failure; } else { int physinif;
/* Case 2: indev is bridge group, we need to look for * physical device (when called from ipv4) */ if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) goto nla_put_failure;
physinif = nf_bridge_get_physinif(entskb); if (physinif && nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, htonl(physinif))) goto nla_put_failure; }#endif }
if (outdev) {#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER) if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) goto nla_put_failure;#else if (entry->state.pf == PF_BRIDGE) { /* Case 1: outdev is physical output device, we need to * look for bridge group (when called from * netfilter_bridge) */ if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, htonl(outdev->ifindex)) || nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_OUTDEV, outdev)) goto nla_put_failure; } else { int physoutif;
/* Case 2: outdev is bridge group, we need to look for * physical output device (when called from ipv4) */ if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) goto nla_put_failure;
physoutif = nf_bridge_get_physoutif(entskb); if (physoutif && nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, htonl(physoutif))) goto nla_put_failure; }#endif }
if (entskb->mark && nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) goto nla_put_failure;
if (entskb->priority && nla_put_be32(skb, NFQA_PRIORITY, htonl(entskb->priority))) goto nla_put_failure;
if (indev && entskb->dev && skb_mac_header_was_set(entskb) && skb_mac_header_len(entskb) != 0) { struct nfqnl_msg_packet_hw phw; int len;
memset(&phw, 0, sizeof(phw)); len = dev_parse_header(entskb, phw.hw_addr); if (len) { phw.hw_addrlen = htons(len); if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) goto nla_put_failure; } }
if (nfqnl_put_bridge(entry, skb) < 0) goto nla_put_failure;
if (entry->state.hook <= NF_INET_FORWARD && tstamp) { struct nfqnl_msg_packet_timestamp ts; struct timespec64 kts = ktime_to_timespec64(tstamp);
ts.sec = cpu_to_be64(kts.tv_sec); ts.usec = cpu_to_be64(kts.tv_nsec / NSEC_PER_USEC);
if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts)) goto nla_put_failure; }
if ((queue->flags & NFQA_CFG_F_UID_GID) && entskb->sk && nfqnl_put_sk_uidgid(skb, entskb->sk) < 0) goto nla_put_failure;
if (nfqnl_put_sk_classid(skb, entskb->sk) < 0) goto nla_put_failure;
if (seclen > 0 && nla_put(skb, NFQA_SECCTX, ctx.len, ctx.context)) goto nla_put_failure;
if (ct && nfnl_ct->build(skb, ct, ctinfo, NFQA_CT, NFQA_CT_INFO) < 0) goto nla_put_failure;
if (cap_len > data_len && nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len))) goto nla_put_failure;
if (nfqnl_put_packet_info(skb, entskb, csum_verify)) goto nla_put_failure;
if (data_len) { struct nlattr *nla;
if (skb_tailroom(skb) < sizeof(*nla) + hlen) goto nla_put_failure;
nla = skb_put(skb, sizeof(*nla)); nla->nla_type = NFQA_PAYLOAD; nla->nla_len = nla_attr_size(data_len);
if (skb_zerocopy(skb, entskb, data_len, hlen)) goto nla_put_failure; }
nlh->nlmsg_len = skb->len; if (seclen >= 0) security_release_secctx(&ctx); return skb;
nla_put_failure: skb_tx_error(entskb); kfree_skb(skb); net_err_ratelimited("nf_queue: error creating packet message\n");nlmsg_failure: if (seclen >= 0) security_release_secctx(&ctx); return NULL;}
static int__nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry){ struct sk_buff *nskb; int err = -ENOBUFS; __be32 *packet_id_ptr; int failopen = 0;
nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr); if (nskb == NULL) { err = -ENOMEM; goto err_out; } spin_lock_bh(&queue->lock);
if (queue->queue_total >= queue->queue_maxlen) goto err_out_queue_drop;
entry->id = ++queue->id_sequence; *packet_id_ptr = htonl(entry->id);
/* Insert into hash BEFORE unicast. If failure don't send to userspace. */ err = __enqueue_entry(queue, entry); if (unlikely(err)) goto err_out_queue_drop;
/* nfnetlink_unicast will either free the nskb or add it to a socket */ err = nfnetlink_unicast(nskb, net, queue->peer_portid); if (err < 0) { /* Unicast failed - remove entry we just inserted */ __dequeue_entry(queue, entry);
if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { failopen = 1; err = 0; } else { queue->queue_user_dropped++; } goto err_out_unlock; }
spin_unlock_bh(&queue->lock); return 0;
err_out_queue_drop: if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { failopen = 1; err = 0; } else { queue->queue_dropped++;
if (queue->queue_total >= queue->queue_maxlen) net_warn_ratelimited("nf_queue: full at %d entries, dropping packets(s)\n", queue->queue_total); else net_warn_ratelimited("nf_queue: hash insert failed: %d\n", err); } kfree_skb(nskb);err_out_unlock: spin_unlock_bh(&queue->lock); if (failopen) nfqnl_reinject(entry, NF_ACCEPT);err_out: return err;}
static struct nf_queue_entry *nf_queue_entry_dup(struct nf_queue_entry *e){ struct nf_queue_entry *entry = kmemdup(e, e->size, GFP_ATOMIC);
if (!entry) return NULL;
if (nf_queue_entry_get_refs(entry)) return entry;
kfree(entry); return NULL;}
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)/* When called from bridge netfilter, skb->data must point to MAC header * before calling skb_gso_segment(). Else, original MAC header is lost * and segmented skbs will be sent to wrong destination. */static void nf_bridge_adjust_skb_data(struct sk_buff *skb){ if (nf_bridge_info_get(skb)) __skb_push(skb, skb_mac_header_len(skb));}
static void nf_bridge_adjust_segmented_data(struct sk_buff *skb){ if (nf_bridge_info_get(skb)) __skb_pull(skb, skb_mac_header_len(skb));}#else#define nf_bridge_adjust_skb_data(s) do {} while (0)#define nf_bridge_adjust_segmented_data(s) do {} while (0)#endif
static int__nfqnl_enqueue_packet_gso(struct net *net, struct nfqnl_instance *queue, struct sk_buff *skb, struct nf_queue_entry *entry){ int ret = -ENOMEM; struct nf_queue_entry *entry_seg;
nf_bridge_adjust_segmented_data(skb);
if (skb->next == NULL) { /* last packet, no need to copy entry */ struct sk_buff *gso_skb = entry->skb; entry->skb = skb; ret = __nfqnl_enqueue_packet(net, queue, entry); if (ret) entry->skb = gso_skb; return ret; }
skb_mark_not_on_list(skb);
entry_seg = nf_queue_entry_dup(entry); if (entry_seg) { entry_seg->skb = skb; ret = __nfqnl_enqueue_packet(net, queue, entry_seg); if (ret) nf_queue_entry_free(entry_seg); } return ret;}
static intnfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum){ struct sk_buff *skb, *segs, *nskb; bool ct_is_unconfirmed = false; struct nfqnl_instance *queue; unsigned int queued; int err = -ENOBUFS; struct net *net = entry->state.net; struct nfnl_queue_net *q = nfnl_queue_pernet(net);
/* rcu_read_lock()ed by nf_hook_thresh */ queue = instance_lookup(q, queuenum); if (!queue) return -ESRCH;
if (queue->copy_mode == NFQNL_COPY_NONE) return -EINVAL;
skb = entry->skb;
switch (entry->state.pf) { case NFPROTO_IPV4: skb->protocol = htons(ETH_P_IP); break; case NFPROTO_IPV6: skb->protocol = htons(ETH_P_IPV6); break; }
/* Check if someone already holds another reference to * unconfirmed ct. If so, we cannot queue the skb: * concurrent modifications of nf_conn->ext are not * allowed and we can't know if another CPU isn't * processing the same nf_conn entry in parallel. */ if (nf_ct_drop_unconfirmed(entry, &ct_is_unconfirmed)) return -EINVAL;
if (!skb_is_gso(skb) || ((queue->flags & NFQA_CFG_F_GSO) && !skb_is_gso_sctp(skb))) return __nfqnl_enqueue_packet(net, queue, entry);
nf_bridge_adjust_skb_data(skb); segs = skb_gso_segment(skb, 0); /* Does not use PTR_ERR to limit the number of error codes that can be * returned by nf_queue. For instance, callers rely on -ESRCH to * mean 'ignore this hook'. */ if (IS_ERR_OR_NULL(segs)) goto out_err; queued = 0; err = 0;
skb_list_walk_safe(segs, segs, nskb) { if (ct_is_unconfirmed && queued > 0) { /* skb_gso_segment() increments the ct refcount. * This is a problem for unconfirmed (not in hash) * entries, those can race when reinjections happen * in parallel. * * Annotate this for all queued entries except the * first one. * * As long as the first one is reinjected first it * will do the confirmation for us. */ entry->nf_ct_is_unconfirmed = ct_is_unconfirmed; }
if (err == 0) err = __nfqnl_enqueue_packet_gso(net, queue, segs, entry); if (err == 0) queued++; else kfree_skb(segs); }
if (queued) { if (err) /* some segments are already queued */ nf_queue_entry_free(entry); kfree_skb(skb); return 0; } out_err: nf_bridge_adjust_segmented_data(skb); return err;}
static bool nfqnl_validate_ipopts(const struct iphdr *iph_new, const struct nf_queue_entry *e){ const struct iphdr *iph_orig = ip_hdr(e->skb); unsigned int ihl = iph_new->ihl * 4;
if (iph_new->ihl != iph_orig->ihl) return false; if (ihl == sizeof(*iph_orig)) return true;
return memcmp(iph_new + 1, ip_hdr(e->skb) + 1, ihl - sizeof(*iph_orig)) == 0;}
static bool nfqnl_validate_l4(const u8 *data, unsigned int data_len, const struct nf_queue_entry *e, u8 proto, bool fragment){#if IS_ENABLED(CONFIG_NF_CONNTRACK) enum ip_conntrack_info ctinfo; const struct nf_conn *ct;
ct = nf_ct_get(e->skb, &ctinfo); if (ct && !nf_ct_is_template(ct)) { if (fragment || nf_ct_protonum(ct) != proto) return false; }#endif
if (fragment) return true;
switch (proto) { case IPPROTO_TCP: { const struct tcphdr *th = (const struct tcphdr *)data; unsigned int thlen;
if (data_len < sizeof(*th)) return false;
thlen = __tcp_hdrlen(th); if (thlen < sizeof(*th) || data_len < thlen) return false;
return true; } case IPPROTO_UDP: return data_len >= sizeof(struct udphdr); case IPPROTO_ICMP: return data_len >= sizeof(struct icmphdr); case IPPROTO_ICMPV6: return data_len >= sizeof(struct icmp6hdr); case IPPROTO_SCTP: return data_len >= sizeof(struct sctphdr); case IPPROTO_GRE: return data_len >= sizeof(struct gre_base_hdr); case IPPROTO_AH: return data_len >= sizeof(struct ip_auth_hdr); case IPPROTO_ESP: return data_len >= sizeof(struct ip_esp_hdr); }
return true;}
static bool nfqnl_validate_ip4(const struct iphdr *iph, unsigned int data_len, const struct nf_queue_entry *e){ unsigned int ihl; bool fragment;
if (data_len < sizeof(*iph)) return false;
ihl = iph->ihl * 4u; if (ihl < sizeof(*iph) || data_len < ihl) return false;
if (iph->version != 4 || ((iph->frag_off ^ ip_hdr(e->skb)->frag_off) & ~htons(IP_DF)) != 0) return false;
/* BIG TCP won't work; netlink attr len is u16 */ if (ntohs(iph->tot_len) != data_len) return false;
fragment = iph->frag_off & htons(IP_MF | IP_OFFSET);
/* support for ipopts mangling would require * recompile + skb transport header update. */ return nfqnl_validate_ipopts(iph, e) && nfqnl_validate_l4((const u8 *)iph + ihl, data_len - ihl, e, iph->protocol, fragment);}
static bool nfqnl_validate_one_exthdr(const u8 *data, unsigned int data_len, const struct nf_queue_entry *e, int start, int hdrlen){ u16 octets;
if (data_len < hdrlen || hdrlen < 2) return false;
while (hdrlen > 0) { if (data_len < sizeof(octets)) return false; data_len -= sizeof(octets);
if (skb_copy_bits(e->skb, start, &octets, sizeof(octets))) return false;
if (hdrlen < sizeof(octets)) return false;
hdrlen -= sizeof(octets); if (memcmp(data, &octets, sizeof(octets))) return false;
start += sizeof(octets); data += sizeof(octets); }
return true;}
static bool nfqnl_validate_exthdr(const struct ipv6hdr *ip6_new, unsigned int data_len, const struct nf_queue_entry *e){ const struct ipv6hdr *ip6_orig = ipv6_hdr(e->skb); int exthdr_cnt = 0, start = sizeof(*ip6_orig); const u8 *data = (const u8 *)ip6_new; u8 orig_nexthdr = ip6_orig->nexthdr; u8 new_nexthdr = ip6_new->nexthdr; bool fragment = false;
if (new_nexthdr != orig_nexthdr) return false;
data += sizeof(*ip6_new); data_len -= sizeof(*ip6_new);
while (ipv6_ext_hdr(orig_nexthdr)) { const struct ipv6_opt_hdr *hp; struct ipv6_opt_hdr _hdr; int hdrlen;
if (orig_nexthdr == NEXTHDR_NONE) return nfqnl_validate_l4(data, data_len, e, new_nexthdr, fragment);
if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) return false;
hp = skb_header_pointer(e->skb, start, sizeof(_hdr), &_hdr); if (!hp) return false;
switch (orig_nexthdr) { case NEXTHDR_FRAGMENT: fragment = true; hdrlen = sizeof(struct frag_hdr); break; case NEXTHDR_AUTH: hdrlen = ipv6_authlen(hp); break; default: hdrlen = ipv6_optlen(hp); break; }
if (!nfqnl_validate_one_exthdr(data, data_len, e, start, hdrlen)) return false;
orig_nexthdr = hp->nexthdr; hp = (const void *)data; new_nexthdr = hp->nexthdr;
if (new_nexthdr != orig_nexthdr) return false;
data_len -= hdrlen; start += hdrlen; data += hdrlen; }
return nfqnl_validate_l4(data, data_len, e, new_nexthdr, fragment);}
static bool nfqnl_validate_ip6(const struct ipv6hdr *ip6, unsigned int data_len, const struct nf_queue_entry *e){ if (data_len < sizeof(*ip6)) return false;
/* BIG TCP/jumbograms won't work; netlink attr len is u16 */ if (ntohs(ip6->payload_len) != data_len - sizeof(*ip6)) return false;
if (ip6->version != 6) return false;
return nfqnl_validate_exthdr(ip6, data_len, e);}
static bool nfqnl_validate_write(const void *data, unsigned int data_len, const struct nf_queue_entry *e){ switch (e->state.pf) { case NFPROTO_IPV4: return nfqnl_validate_ip4(data, data_len, e); case NFPROTO_IPV6: return nfqnl_validate_ip6(data, data_len, e) && !(IP6CB(e->skb)->flags & IP6SKB_JUMBOGRAM); case NFPROTO_BRIDGE: /* No write support. Bridge is dubious: userspace doesn't even see L2 header */ return false; }
return false;}
static intnfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int diff){ struct sk_buff *nskb;
if (e->state.net->user_ns != &init_user_ns) return -EPERM;
if (!nfqnl_validate_write(data, data_len, e)) return -EINVAL;
if (diff < 0) { unsigned int min_len = skb_transport_offset(e->skb);
if (data_len < min_len) return -EINVAL;
if (pskb_trim(e->skb, data_len)) return -ENOMEM; } else if (diff > 0) { if (data_len > 0xFFFF) return -EINVAL; if (diff > skb_tailroom(e->skb)) { nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), diff, GFP_ATOMIC); if (!nskb) return -ENOMEM; kfree_skb(e->skb); e->skb = nskb; } skb_put(e->skb, diff); } if (skb_ensure_writable(e->skb, data_len)) return -ENOMEM; skb_copy_to_linear_data(e->skb, data, data_len); e->skb->ip_summed = CHECKSUM_NONE; return 0;}
static intnfqnl_set_mode(struct nfqnl_instance *queue, unsigned char mode, unsigned int range){ int status = 0;
spin_lock_bh(&queue->lock); switch (mode) { case NFQNL_COPY_NONE: case NFQNL_COPY_META: queue->copy_mode = mode; queue->copy_range = 0; break;
case NFQNL_COPY_PACKET: queue->copy_mode = mode; if (range == 0 || range > NFQNL_MAX_COPY_RANGE) queue->copy_range = NFQNL_MAX_COPY_RANGE; else queue->copy_range = range; break;
default: status = -EINVAL;
} spin_unlock_bh(&queue->lock);
return status;}
static intdev_cmp(struct nf_queue_entry *entry, unsigned long ifindex){#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) int physinif, physoutif;
physinif = nf_bridge_get_physinif(entry->skb); physoutif = nf_bridge_get_physoutif(entry->skb);
if (physinif == ifindex || physoutif == ifindex) return 1;
if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) return 1;#endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) return 1; if (entry->state.in) if (entry->state.in->ifindex == ifindex) return 1; if (entry->state.out) if (entry->state.out->ifindex == ifindex) return 1;
return 0;}
/* drop all packets with either indev or outdev == ifindex from all queue * instances */static voidnfqnl_dev_drop(struct net *net, int ifindex){ int i; struct nfnl_queue_net *q = nfnl_queue_pernet(net);
rcu_read_lock();
for (i = 0; i < INSTANCE_BUCKETS; i++) { struct nfqnl_instance *inst; struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_rcu(inst, head, hlist) nfqnl_flush(inst, dev_cmp, ifindex); }
rcu_read_unlock();}
static intnfqnl_rcv_dev_event(struct notifier_block *this, unsigned long event, void *ptr){ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
/* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) nfqnl_dev_drop(dev_net(dev), dev->ifindex); return NOTIFY_DONE;}
static struct notifier_block nfqnl_dev_notifier = { .notifier_call = nfqnl_rcv_dev_event,};
/* Match the entries whose hook_index points into the hook array that * __nf_unregister_net_hook() replaces when it removes @ops. * * ops->pf/hooknum name the hook as registered; entry->state.pf/hook * name the array actually traversed - they differ only for NFPROTO_INET. * Every other family uses the array of its own name. */static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr){ const struct nf_hook_ops *ops = (const struct nf_hook_ops *)ops_ptr; unsigned int hooknum = ops->hooknum; u8 pf = ops->pf;
/* same translation as nf_static_key_inc() */ if (pf == NFPROTO_INET && hooknum == NF_INET_INGRESS) { pf = NFPROTO_NETDEV; hooknum = NF_NETDEV_INGRESS; }
if (entry->state.hook != hooknum) return 0;
if (pf == NFPROTO_INET) return entry->state.pf == NFPROTO_IPV4 || entry->state.pf == NFPROTO_IPV6;
return entry->state.pf == pf;}
static void nfqnl_nf_hook_drop(struct net *net, const struct nf_hook_ops *ops){ struct nfnl_queue_net *q = nfnl_queue_pernet(net); nfqnl_cmpfn cmpfn = NULL; int i;
/* This function is also called on net namespace error unwind, * when pernet_ops->init() failed and ->exit() functions of the * previous pernet_ops gets called. * * This may result in a call to nfqnl_nf_hook_drop() before * struct nfnl_queue_net was allocated. */ if (!q) return;
/* ops is NULL when the caller is not unregistering a hook but * removing an object queued packets may reference, every entry * has to go */ if (ops) cmpfn = nf_hook_cmp;
for (i = 0; i < INSTANCE_BUCKETS; i++) { struct nfqnl_instance *inst; struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_rcu(inst, head, hlist) nfqnl_flush(inst, cmpfn, (unsigned long)ops); }}
static intnfqnl_rcv_nl_event(struct notifier_block *this, unsigned long event, void *ptr){ struct netlink_notify *n = ptr; struct nfnl_queue_net *q = nfnl_queue_pernet(n->net);
if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { int i;
nfnl_lock(NFNL_SUBSYS_QUEUE); /* destroy all instances for this portid */ spin_lock(&q->instances_lock); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *t2; struct nfqnl_instance *inst; struct hlist_head *head = &q->instance_table[i];
hlist_for_each_entry_safe(inst, t2, head, hlist) { if (n->portid == inst->peer_portid) __instance_destroy(inst); } } spin_unlock(&q->instances_lock); nfnl_unlock(NFNL_SUBSYS_QUEUE); } return NOTIFY_DONE;}
static struct notifier_block nfqnl_rtnl_notifier = { .notifier_call = nfqnl_rcv_nl_event,};
static const struct nla_policy nfqa_vlan_policy[NFQA_VLAN_MAX + 1] = { [NFQA_VLAN_TCI] = { .type = NLA_U16}, [NFQA_VLAN_PROTO] = { .type = NLA_U16},};
static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, [NFQA_MARK] = { .type = NLA_U32 }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, [NFQA_CT] = { .type = NLA_UNSPEC }, [NFQA_EXP] = { .type = NLA_UNSPEC }, [NFQA_VLAN] = { .type = NLA_NESTED }, [NFQA_PRIORITY] = { .type = NLA_U32 },};
static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, [NFQA_MARK] = { .type = NLA_U32 }, [NFQA_PRIORITY] = { .type = NLA_U32 },};
static struct nfqnl_instance *verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, u32 nlportid){ struct nfqnl_instance *queue;
queue = instance_lookup(q, queue_num); if (!queue) return ERR_PTR(-ENODEV);
if (queue->peer_portid != nlportid) return ERR_PTR(-EPERM);
return queue;}
static struct nfqnl_msg_verdict_hdr*verdicthdr_get(const struct nlattr * const nfqa[]){ struct nfqnl_msg_verdict_hdr *vhdr; unsigned int verdict;
if (!nfqa[NFQA_VERDICT_HDR]) return NULL;
vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) return NULL; return vhdr;}
static int nfq_id_after(unsigned int id, unsigned int max){ return (int)(id - max) > 0;}
static int nfqnl_recv_verdict_batch(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nfqa[]){ struct nfnl_queue_net *q = nfnl_queue_pernet(info->net); u16 queue_num = ntohs(info->nfmsg->res_id); struct nf_queue_entry *entry, *tmp; struct nfqnl_msg_verdict_hdr *vhdr; struct nfqnl_instance *queue; unsigned int verdict, maxid; LIST_HEAD(batch_list);
queue = verdict_instance_lookup(q, queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue);
vhdr = verdicthdr_get(nfqa); if (!vhdr) return -EINVAL;
verdict = ntohl(vhdr->verdict); maxid = ntohl(vhdr->id);
spin_lock_bh(&queue->lock);
list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { if (nfq_id_after(entry->id, maxid)) break; __dequeue_entry(queue, entry); list_add_tail(&entry->list, &batch_list); }
spin_unlock_bh(&queue->lock);
if (list_empty(&batch_list)) return -ENOENT;
list_for_each_entry_safe(entry, tmp, &batch_list, list) { if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));
if (nfqa[NFQA_PRIORITY]) entry->skb->priority = ntohl(nla_get_be32(nfqa[NFQA_PRIORITY]));
nfqnl_reinject(entry, verdict); } return 0;}
static struct nf_conn *nfqnl_ct_parse(const struct nfnl_ct_hook *nfnl_ct, const struct nlmsghdr *nlh, const struct nlattr * const nfqa[], struct nf_queue_entry *entry, enum ip_conntrack_info *ctinfo){#if IS_ENABLED(CONFIG_NF_CONNTRACK) struct nf_conn *ct;
ct = nf_ct_get(entry->skb, ctinfo); if (ct == NULL) return NULL;
if (nfnl_ct->parse(nfqa[NFQA_CT], ct) < 0) return NULL;
if (nfqa[NFQA_EXP]) nfnl_ct->attach_expect(nfqa[NFQA_EXP], ct, NETLINK_CB(entry->skb).portid, nlmsg_report(nlh)); return ct;#else return NULL;#endif}
static int nfqa_parse_bridge(struct nf_queue_entry *entry, const struct nlattr * const nfqa[]){ if (nfqa[NFQA_VLAN]) { struct nlattr *tb[NFQA_VLAN_MAX + 1]; int err;
err = nla_parse_nested_deprecated(tb, NFQA_VLAN_MAX, nfqa[NFQA_VLAN], nfqa_vlan_policy, NULL); if (err < 0) return err;
if (!tb[NFQA_VLAN_TCI] || !tb[NFQA_VLAN_PROTO]) return -EINVAL;
__vlan_hwaccel_put_tag(entry->skb, nla_get_be16(tb[NFQA_VLAN_PROTO]), ntohs(nla_get_be16(tb[NFQA_VLAN_TCI]))); }
if (nfqa[NFQA_L2HDR]) { u32 mac_header_len = skb_mac_header_len(entry->skb);
if (mac_header_len != nla_len(nfqa[NFQA_L2HDR])) return -EINVAL; else if (mac_header_len > 0) memcpy(skb_mac_header(entry->skb), nla_data(nfqa[NFQA_L2HDR]), mac_header_len); }
return 0;}
static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nfqa[]){ struct nfnl_queue_net *q = nfnl_queue_pernet(info->net); u_int16_t queue_num = ntohs(info->nfmsg->res_id); const struct nfnl_ct_hook *nfnl_ct; struct nfqnl_msg_verdict_hdr *vhdr; enum ip_conntrack_info ctinfo; struct nfqnl_instance *queue; struct nf_queue_entry *entry; struct nf_conn *ct = NULL; unsigned int verdict; int err;
queue = verdict_instance_lookup(q, queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue);
vhdr = verdicthdr_get(nfqa); if (!vhdr) return -EINVAL;
verdict = ntohl(vhdr->verdict);
entry = find_dequeue_entry(queue, ntohl(vhdr->id)); if (entry == NULL) return -ENOENT;
/* rcu lock already held from nfnl->call_rcu. */ nfnl_ct = rcu_dereference(nfnl_ct_hook);
if (nfqa[NFQA_CT]) { if (nfnl_ct != NULL) ct = nfqnl_ct_parse(nfnl_ct, info->nlh, nfqa, entry, &ctinfo); }
if (entry->state.pf == PF_BRIDGE) { err = nfqa_parse_bridge(entry, nfqa); if (err < 0) { nfqnl_reinject(entry, NF_DROP); return err; } }
if (nfqa[NFQA_PAYLOAD]) { u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); int diff = payload_len - entry->skb->len;
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), payload_len, entry, diff) < 0) verdict = NF_DROP; else if (ct && diff) nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff); }
if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));
if (nfqa[NFQA_PRIORITY]) entry->skb->priority = ntohl(nla_get_be32(nfqa[NFQA_PRIORITY]));
nfqnl_reinject(entry, verdict); return 0;}
static int nfqnl_recv_unsupp(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const cda[]){ return -ENOTSUPP;}
static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, [NFQA_CFG_QUEUE_MAXLEN] = { .type = NLA_U32 }, [NFQA_CFG_MASK] = { .type = NLA_U32 }, [NFQA_CFG_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFQA_CFG_F_MAX - 1),};
static const struct nf_queue_handler nfqh = { .outfn = nfqnl_enqueue_packet, .nf_hook_drop = nfqnl_nf_hook_drop,};
static int nfqnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nfqa[]){ struct nfnl_queue_net *q = nfnl_queue_pernet(info->net); u_int16_t queue_num = ntohs(info->nfmsg->res_id); struct nfqnl_msg_config_cmd *cmd = NULL; struct nfqnl_instance *queue; __u32 flags = 0, mask = 0;
WARN_ON_ONCE(!lockdep_nfnl_is_held(NFNL_SUBSYS_QUEUE));
if (nfqa[NFQA_CFG_CMD]) { cmd = nla_data(nfqa[NFQA_CFG_CMD]);
/* Obsolete commands without queue context */ switch (cmd->command) { case NFQNL_CFG_CMD_PF_BIND: return 0; case NFQNL_CFG_CMD_PF_UNBIND: return 0; } }
/* Check if we support these flags in first place, dependencies should * be there too not to break atomicity. */ if (nfqa[NFQA_CFG_FLAGS]) { if (!nfqa[NFQA_CFG_MASK]) { /* A mask is needed to specify which flags are being * changed. */ return -EINVAL; }
flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK]));
if (flags >= NFQA_CFG_F_MAX) return -EOPNOTSUPP;
#if !IS_ENABLED(CONFIG_NETWORK_SECMARK) if (flags & mask & NFQA_CFG_F_SECCTX) return -EOPNOTSUPP;#endif if ((flags & mask & NFQA_CFG_F_CONNTRACK) && !rcu_access_pointer(nfnl_ct_hook)) {#ifdef CONFIG_MODULES nfnl_unlock(NFNL_SUBSYS_QUEUE); request_module("ip_conntrack_netlink"); nfnl_lock(NFNL_SUBSYS_QUEUE); if (rcu_access_pointer(nfnl_ct_hook)) return -EAGAIN;#endif return -EOPNOTSUPP; } }
/* Lookup queue under RCU. After peer_portid check (or for new queue * in BIND case), the queue is owned by the socket sending this message. * nfqnl_rcv_nl_event() will block on the nfnl subsys mutex that is * held by the caller, so the queue cannot be destroyed in parallel, * even after we drop the RCU read lock. */ rcu_read_lock(); queue = instance_lookup(q, queue_num); if (queue && queue->peer_portid != NETLINK_CB(skb).portid) { rcu_read_unlock(); return -EPERM; } rcu_read_unlock();
if (cmd != NULL) { switch (cmd->command) { case NFQNL_CFG_CMD_BIND: if (queue) return -EBUSY; queue = instance_create(q, queue_num, NETLINK_CB(skb).portid); if (IS_ERR(queue)) return PTR_ERR(queue); break; case NFQNL_CFG_CMD_UNBIND: if (!queue) return -ENODEV; instance_destroy(q, queue); return 0; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_UNBIND: break; default: return -EOPNOTSUPP; } }
if (!queue) return -ENODEV;
if (nfqa[NFQA_CFG_PARAMS]) { struct nfqnl_msg_config_params *params = nla_data(nfqa[NFQA_CFG_PARAMS]);
nfqnl_set_mode(queue, params->copy_mode, ntohl(params->copy_range)); }
if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { __be32 *queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]);
spin_lock_bh(&queue->lock); queue->queue_maxlen = ntohl(*queue_maxlen); spin_unlock_bh(&queue->lock); }
if (nfqa[NFQA_CFG_FLAGS]) { spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; spin_unlock_bh(&queue->lock); }
return 0;}
static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { [NFQNL_MSG_PACKET] = { .call = nfqnl_recv_unsupp, .type = NFNL_CB_RCU, .attr_count = NFQA_MAX, }, [NFQNL_MSG_VERDICT] = { .call = nfqnl_recv_verdict, .type = NFNL_CB_RCU, .attr_count = NFQA_MAX, .policy = nfqa_verdict_policy }, [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, .type = NFNL_CB_MUTEX, .attr_count = NFQA_CFG_MAX, .policy = nfqa_cfg_policy }, [NFQNL_MSG_VERDICT_BATCH] = { .call = nfqnl_recv_verdict_batch, .type = NFNL_CB_RCU, .attr_count = NFQA_MAX, .policy = nfqa_verdict_batch_policy },};
static const struct nfnetlink_subsystem nfqnl_subsys = { .name = "nf_queue", .subsys_id = NFNL_SUBSYS_QUEUE, .cb_count = NFQNL_MSG_MAX, .cb = nfqnl_cb,};
#ifdef CONFIG_PROC_FSstruct iter_state { struct seq_net_private p; unsigned int bucket;};
static struct hlist_node *get_first(struct seq_file *seq){ struct iter_state *st = seq->private; struct net *net; struct nfnl_queue_net *q;
if (!st) return NULL;
net = seq_file_net(seq); q = nfnl_queue_pernet(net); for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { if (!hlist_empty(&q->instance_table[st->bucket])) return q->instance_table[st->bucket].first; } return NULL;}
static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h){ struct iter_state *st = seq->private; struct net *net = seq_file_net(seq);
h = h->next; while (!h) { struct nfnl_queue_net *q;
if (++st->bucket >= INSTANCE_BUCKETS) return NULL;
q = nfnl_queue_pernet(net); h = q->instance_table[st->bucket].first; } return h;}
static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos){ struct hlist_node *head; head = get_first(seq);
if (head) while (pos && (head = get_next(seq, head))) pos--; return pos ? NULL : head;}
static void *seq_start(struct seq_file *s, loff_t *pos) __acquires(nfnl_queue_pernet(seq_file_net(s))->instances_lock){ spin_lock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock); return get_idx(s, *pos);}
static void *seq_next(struct seq_file *s, void *v, loff_t *pos){ (*pos)++; return get_next(s, v);}
static void seq_stop(struct seq_file *s, void *v) __releases(nfnl_queue_pernet(seq_file_net(s))->instances_lock){ spin_unlock(&nfnl_queue_pernet(seq_file_net(s))->instances_lock);}
static int seq_show(struct seq_file *s, void *v){ const struct nfqnl_instance *inst = v;
seq_printf(s, "%5u %6u %5u %1u %5u %5u %5u %8u %2d\n", inst->queue_num, inst->peer_portid, inst->queue_total, inst->copy_mode, inst->copy_range, inst->queue_dropped, inst->queue_user_dropped, inst->id_sequence, 1); return 0;}
static const struct seq_operations nfqnl_seq_ops = { .start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show,};#endif /* PROC_FS */
static int __net_init nfnl_queue_net_init(struct net *net){ unsigned int i; struct nfnl_queue_net *q = nfnl_queue_pernet(net);
for (i = 0; i < INSTANCE_BUCKETS; i++) INIT_HLIST_HEAD(&q->instance_table[i]);
spin_lock_init(&q->instances_lock);
#ifdef CONFIG_PROC_FS if (!proc_create_net("nfnetlink_queue", 0440, net->nf.proc_netfilter, &nfqnl_seq_ops, sizeof(struct iter_state))) return -ENOMEM;#endif return 0;}
static void __net_exit nfnl_queue_net_exit(struct net *net){ struct nfnl_queue_net *q = nfnl_queue_pernet(net); unsigned int i;
#ifdef CONFIG_PROC_FS remove_proc_entry("nfnetlink_queue", net->nf.proc_netfilter);#endif for (i = 0; i < INSTANCE_BUCKETS; i++) WARN_ON_ONCE(!hlist_empty(&q->instance_table[i]));}
static struct pernet_operations nfnl_queue_net_ops = { .init = nfnl_queue_net_init, .exit = nfnl_queue_net_exit, .id = &nfnl_queue_net_id, .size = sizeof(struct nfnl_queue_net),};
static int __init nfnetlink_queue_init(void){ int status;
nfq_cleanup_wq = alloc_ordered_workqueue("nfq_workqueue", 0); if (!nfq_cleanup_wq) return -ENOMEM;
status = register_pernet_subsys(&nfnl_queue_net_ops); if (status < 0) goto cleanup_pernet_subsys;
status = netlink_register_notifier(&nfqnl_rtnl_notifier); if (status < 0) goto cleanup_rtnl_notifier;
status = register_netdevice_notifier(&nfqnl_dev_notifier); if (status < 0) goto cleanup_dev_notifier;
status = nfnetlink_subsys_register(&nfqnl_subsys); if (status < 0) goto cleanup_nfqnl_subsys;
nf_register_queue_handler(&nfqh);
return status;
cleanup_nfqnl_subsys: unregister_netdevice_notifier(&nfqnl_dev_notifier);cleanup_dev_notifier: netlink_unregister_notifier(&nfqnl_rtnl_notifier);cleanup_rtnl_notifier: unregister_pernet_subsys(&nfnl_queue_net_ops);cleanup_pernet_subsys: destroy_workqueue(nfq_cleanup_wq); return status;}
static void __exit nfnetlink_queue_fini(void){ nf_unregister_queue_handler(); unregister_netdevice_notifier(&nfqnl_dev_notifier); nfnetlink_subsys_unregister(&nfqnl_subsys); netlink_unregister_notifier(&nfqnl_rtnl_notifier); unregister_pernet_subsys(&nfnl_queue_net_ops); destroy_workqueue(nfq_cleanup_wq); rcu_barrier(); /* Wait for completion of call_rcu()'s */}
MODULE_DESCRIPTION("netfilter packet queue handler");MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");MODULE_LICENSE("GPL");MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE);
module_init(nfnetlink_queue_init);module_exit(nfnetlink_queue_fini);