Import of kernel-6.12.0-211.47.1.el10_2

This commit is contained in:
almalinux-bot-kernel 2026-08-15 04:15:12 +00:00
parent 55d3eb33dc
commit 1d2c3ecdad
97 changed files with 1769 additions and 603 deletions

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 2
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 211.46.1
RHEL_RELEASE = 211.47.1
#
# RHEL_REBASE_NUM

View File

@ -1761,7 +1761,7 @@ struct kvm_x86_ops {
* Can potentially get non-canonical addresses through INVLPGs, which
* the implementation may choose to ignore if appropriate.
*/
void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr);
void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr, bool *full);
/*
* Flush any TLB entries created by the guest. Like tlb_flush_gva(),

View File

@ -1971,6 +1971,7 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
u64 entries[KVM_HV_TLB_FLUSH_FIFO_SIZE];
int i, j, count;
gva_t gva;
bool full = false;
if (!tdp_enabled || !hv_vcpu)
return -EINVAL;
@ -1979,20 +1980,21 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
count = kfifo_out(&tlb_flush_fifo->entries, entries, KVM_HV_TLB_FLUSH_FIFO_SIZE);
for (i = 0; i < count; i++) {
for (i = 0; i < count && !full; i++) {
if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY)
goto out_flush_all;
if (is_noncanonical_invlpg_address(entries[i], vcpu))
continue;
/*
* Lower 12 bits of 'address' encode the number of additional
* pages to flush.
*/
gva = entries[i] & PAGE_MASK;
for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++)
kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE);
for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1 && !full; j++) {
if (is_noncanonical_invlpg_address(gva + j * PAGE_SIZE, vcpu))
continue;
kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE, &full);
}
++vcpu->stat.tlb_flush;
}

View File

@ -6434,7 +6434,7 @@ void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
if (is_noncanonical_invlpg_address(addr, vcpu))
return;
kvm_x86_call(flush_tlb_gva)(vcpu, addr);
kvm_x86_call(flush_tlb_gva)(vcpu, addr, NULL);
}
if (!mmu->sync_spte)
@ -7163,13 +7163,19 @@ restart:
sp = sptep_to_sp(sptep);
/*
* We cannot do huge page mapping for indirect shadow pages,
* which are found on the last rmap (level = 1) when not using
* tdp; such shadow pages are synced with the page table in
* the guest, and the guest page table is using 4K page size
* mapping if the indirect sp has level = 1.
* Direct shadow page can be replaced by a hugepage if the host
* mapping level allows it and the memslot maps all of the host
* hugepage. Note! If the memslot maps only part of the
* hugepage, sp->gfn may be below slot->base_gfn, and querying
* the max mapping level would cause an out-of-bounds lpage_info
* access. So the gfn bounds check *must* be done first.
*
* Indirect shadow pages are created when the guest page tables
* are using 4K pages. Since the host mapping is always
* constrained by the page size in the guest, indirect shadow
* pages are never collapsible.
*/
if (sp->role.direct &&
if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) &&
sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn)) {
kvm_zap_one_rmap_spte(kvm, rmap_head, sptep);

View File

@ -4021,11 +4021,24 @@ static void svm_flush_tlb_all(struct kvm_vcpu *vcpu)
svm_flush_tlb_asid(vcpu);
}
static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva)
static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva, bool *full)
{
struct vcpu_svm *svm = to_svm(vcpu);
invlpga(gva, svm->vmcb->control.asid);
/*
* INVLPGA has had errata on Genoa and Turin, and even on older
* generations there were reports of Windows BSODs if INVLPGA
* was used for Hyper-V tlbflush. Use it only for shadow paging
* where it seems to be okay.
*/
if (!npt_enabled) {
invlpga(gva, svm->vmcb->control.asid);
return;
}
svm_flush_tlb_asid(vcpu);
if (full)
*full = true;
}
static inline void sync_cr8_to_lapic(struct kvm_vcpu *vcpu)

View File

@ -530,12 +530,12 @@ static void vt_flush_tlb_current(struct kvm_vcpu *vcpu)
vmx_flush_tlb_current(vcpu);
}
static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full)
{
if (is_td_vcpu(vcpu))
return;
vmx_flush_tlb_gva(vcpu, addr);
vmx_flush_tlb_gva(vcpu, addr, full);
}
static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu)

View File

@ -331,6 +331,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
static void free_nested(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
struct vmcs *shadow_vmcs;
if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
vmx_switch_vmcs(vcpu, &vmx->vmcs01);
@ -348,9 +349,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
vmx->nested.current_vmptr = INVALID_GPA;
if (enable_shadow_vmcs) {
vmx_disable_shadow_vmcs(vmx);
vmcs_clear(vmx->vmcs01.shadow_vmcs);
free_vmcs(vmx->vmcs01.shadow_vmcs);
/*
* Keep the pointer visible until after VMCLEAR, so migration
* can clear an active shadow VMCS on the old CPU.
*/
shadow_vmcs = vmx->vmcs01.shadow_vmcs;
vmcs_clear(shadow_vmcs);
vmx->vmcs01.shadow_vmcs = NULL;
free_vmcs(shadow_vmcs);
}
kfree(vmx->nested.cached_vmcs12);
vmx->nested.cached_vmcs12 = NULL;
@ -3661,6 +3668,8 @@ vmentry_fail_vmexit:
if (!from_vmentry)
return NVMX_VMENTRY_VMEXIT;
nested_put_vmcs12_pages(vcpu);
load_vmcs12_host_state(vcpu, vmcs12);
vmcs12->vm_exit_reason = exit_reason.full;
if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx))

View File

@ -3202,7 +3202,7 @@ void vmx_flush_tlb_current(struct kvm_vcpu *vcpu)
vpid_sync_context(vmx_get_current_vpid(vcpu));
}
void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full)
{
/*
* vpid_sync_vcpu_addr() is a nop if vpid==0, see the comment in

View File

@ -82,7 +82,7 @@ void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
bool vmx_get_if_flag(struct kvm_vcpu *vcpu);
void vmx_flush_tlb_all(struct kvm_vcpu *vcpu);
void vmx_flush_tlb_current(struct kvm_vcpu *vcpu);
void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr);
void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full);
void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu);
void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask);
u32 vmx_get_interrupt_shadow(struct kvm_vcpu *vcpu);

View File

@ -276,7 +276,7 @@ int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
if (ipc_buf)
memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf));
if (rx_msg->jsm_msg) {
u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg));
u32 size = min(rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg));
if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) {
ivpu_err(vdev, "IPC resp result error: %d\n", rx_msg->jsm_msg->result);

View File

@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero(
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0)
if (ret < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_INIT_ERR);
return -1;
}
if (!iscsi_check_negotiated_keys(conn->param_list)) {
bool auth_required = iscsi_conn_auth_required(conn);
@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0) {

View File

@ -1371,19 +1371,42 @@ free_buffer:
return -1;
}
/*
* Append "key=value" plus a trailing NUL into @textbuf at *@length.
* Returns 0 on success and advances *@length, or -EMSGSIZE if the
* record (including the NUL) would not fit in the remaining buffer.
*/
static int iscsi_encode_text_record(char *textbuf, u32 *length,
u32 textbuf_size,
const char *key, const char *value)
{
int n;
u32 avail;
if (*length >= textbuf_size)
return -EMSGSIZE;
avail = textbuf_size - *length;
n = snprintf(textbuf + *length, avail, "%s=%s", key, value);
if (n < 0 || (u32)n + 1 > avail)
return -EMSGSIZE;
*length += n + 1;
return 0;
}
int iscsi_encode_text_output(
u8 phase,
u8 sender,
char *textbuf,
u32 *length,
u32 textbuf_size,
struct iscsi_param_list *param_list,
bool keys_workaround)
{
char *output_buf = NULL;
struct iscsi_extra_response *er;
struct iscsi_param *param;
output_buf = textbuf + *length;
int ret;
if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
return -1;
@ -1395,10 +1418,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_RESPONSE_SENT(param) &&
!IS_PSTATE_REPLY_OPTIONAL(param) &&
(param->phase & phase)) {
*length += sprintf(output_buf, "%s=%s",
param->name, param->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length,
textbuf_size,
param->name,
param->value);
if (ret < 0)
goto err_overflow;
SET_PSTATE_RESPONSE_SENT(param);
pr_debug("Sending key: %s=%s\n",
param->name, param->value);
@ -1408,10 +1433,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_ACCEPTOR(param) &&
!IS_PSTATE_PROPOSER(param) &&
(param->phase & phase)) {
*length += sprintf(output_buf, "%s=%s",
param->name, param->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length,
textbuf_size,
param->name,
param->value);
if (ret < 0)
goto err_overflow;
SET_PSTATE_PROPOSER(param);
iscsi_check_proposer_for_optional_reply(param,
keys_workaround);
@ -1421,14 +1448,21 @@ int iscsi_encode_text_output(
}
list_for_each_entry(er, &param_list->extra_response_list, er_list) {
*length += sprintf(output_buf, "%s=%s", er->key, er->value);
*length += 1;
output_buf = textbuf + *length;
ret = iscsi_encode_text_record(textbuf, length, textbuf_size,
er->key, er->value);
if (ret < 0)
goto err_overflow;
pr_debug("Sending key: %s=%s\n", er->key, er->value);
}
iscsi_release_extra_responses(param_list);
return 0;
err_overflow:
pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n",
textbuf_size);
iscsi_release_extra_responses(param_list);
return -1;
}
int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)

View File

@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_
extern int iscsi_extract_key_value(char *, char **, char **);
extern int iscsi_update_param_value(struct iscsi_param *, char *);
extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *);
extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32,
struct iscsi_param_list *, bool);
extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,

View File

@ -1768,6 +1768,11 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
struct kvm_irq_ack_notifier *kian);
bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args);
static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn)
{
return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages;
}
/*
* Returns a pointer to the memslot if it contains gfn.
* Otherwise returns NULL.
@ -1778,7 +1783,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
if (!slot)
return NULL;
if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages)
if (is_gfn_in_memslot(slot, gfn))
return slot;
else
return NULL;

View File

@ -309,7 +309,7 @@ enum {
/* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(struct net *net,
const char *name, struct ip_set **set);
const struct nlattr *name, struct ip_set **set);
extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name);
extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);

View File

@ -21,9 +21,10 @@ struct nf_ct_gre_keymap {
struct rcu_head rcu;
};
/* add new tuple->key_reply pair to keymap */
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
struct nf_conntrack_tuple *t);
/* add tuple->key_reply pairs to keymap */
bool nf_ct_gre_keymap_add(struct nf_conn *ct,
const struct nf_conntrack_tuple *orig,
const struct nf_conntrack_tuple *repl);
/* delete keymap entries */
void nf_ct_gre_keymap_destroy(struct nf_conn *ct);

View File

@ -156,6 +156,9 @@ struct xt_match {
/* Called when user tries to insert an entry of this type. */
int (*checkentry)(const struct xt_mtchk_param *);
/* Called to validate hooks based on the match configuration. */
int (*check_hooks)(const struct xt_mtchk_param *);
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_mtdtor_param *);
#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
@ -197,6 +200,9 @@ struct xt_target {
/* Should return 0 on success or an error code otherwise (-Exxxx). */
int (*checkentry)(const struct xt_tgchk_param *);
/* Called to validate hooks based on the target configuration. */
int (*check_hooks)(const struct xt_tgchk_param *);
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_tgdtor_param *);
#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
@ -289,8 +295,10 @@ bool xt_find_jump_offset(const unsigned int *offsets,
int xt_check_proc_name(const char *name, unsigned int size);
int xt_check_hooks_match(struct xt_mtchk_param *par);
int xt_check_match(struct xt_mtchk_param *, unsigned int size, u16 proto,
bool inv_proto);
int xt_check_hooks_target(struct xt_tgchk_param *par);
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u16 proto,
bool inv_proto);

View File

@ -251,6 +251,35 @@ static inline void list_replace_rcu(struct list_head *old,
old->prev = LIST_POISON2;
}
static inline void __list_splice_rcu(struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
last->next = next;
first->prev = prev;
next->prev = last;
rcu_assign_pointer(list_next_rcu(prev), first);
}
/**
* list_splice_rcu - splice a non-RCU list into an RCU-protected list,
* designed for stacks.
* @list: the non RCU-protected list to splice
* @head: the place in the existing RCU-protected list to splice
*
* The list pointed to by @head can be RCU-read traversed concurrently with
* this function.
*/
static inline void list_splice_rcu(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice_rcu(list, head, head->next);
}
/**
* __list_splice_init_rcu - join an RCU-protected list into an existing list.
* @list: the RCU-protected list to splice

View File

@ -475,11 +475,15 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
{
unsigned int seq, hh_alen;
unsigned int seq, hh_alen = HH_DATA_ALIGN(ETH_HLEN);
int err;
err = skb_cow_head(skb, hh_alen);
if (err)
return err;
do {
seq = read_seqbegin(&hh->hh_lock);
hh_alen = HH_DATA_ALIGN(ETH_HLEN);
memcpy(skb->data - hh_alen, hh->hh_data, ETH_ALEN + hh_alen - ETH_HLEN);
} while (read_seqretry(&hh->hh_lock, seq));
return 0;

View File

@ -39,8 +39,8 @@ struct nf_conntrack_expect {
void (*expectfn)(struct nf_conn *new,
struct nf_conntrack_expect *this);
/* Helper to assign to new connection */
struct nf_conntrack_helper *helper;
/* Helper that created this expectation */
struct nf_conntrack_helper __rcu *helper;
/* The conntrack of the master connection */
struct nf_conn *master;
@ -58,11 +58,30 @@ struct nf_conntrack_expect {
#endif
struct rcu_head rcu;
/* Network namespace */
RH_KABI_EXTEND(possible_net_t net)
#ifdef CONFIG_NF_CONNTRACK_ZONES
RH_KABI_EXTEND(struct nf_conntrack_zone zone)
#endif
/* Helper to assign to new connection */
RH_KABI_EXTEND(struct nf_conntrack_helper __rcu *assign_helper)
RH_KABI_EXTEND(struct nf_conntrack_tuple master_tuple)
};
static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
{
return nf_ct_net(exp->master);
return read_pnet(&exp->net);
}
static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a,
const struct nf_conntrack_zone *b)
{
#ifdef CONFIG_NF_CONNTRACK_ZONES
return a->zone.id == b->id;
#else
return true;
#endif
}
#define NF_CT_EXP_POLICY_NAME_LEN 16

View File

@ -155,6 +155,7 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n);
void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n);
void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n);
struct nf_ct_helper_expectfn *
nf_ct_helper_expectfn_find_by_name(const char *name);
struct nf_ct_helper_expectfn *

View File

@ -14,6 +14,7 @@
struct nf_ct_timeout {
__u16 l3num;
const struct nf_conntrack_l4proto *l4proto;
struct rcu_head rcu;
char data[];
};

View File

@ -12,6 +12,7 @@
struct nf_queue_entry {
struct list_head list;
struct sk_buff *skb;
struct net_device *skb_dev;
unsigned int id;
unsigned int hook_index; /* index in hook_entries->hook[] */
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)

View File

@ -871,6 +871,8 @@ struct nft_elem_priv *nft_set_elem_init(const struct nft_set *set,
u64 timeout, u64 expiration, gfp_t gfp);
int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_expr *expr_array[]);
void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_set_elem_expr *elem_expr);
void nft_set_elem_destroy(const struct nft_set *set,
const struct nft_elem_priv *elem_priv,
bool destroy_expr);
@ -1211,12 +1213,15 @@ struct nft_stats {
struct u64_stats_sync syncp;
};
#define NFT_HOOK_REMOVE (1 << 0)
struct nft_hook {
struct list_head list;
struct list_head ops_list;
struct rcu_head rcu;
char ifname[IFNAMSIZ];
u8 ifnamelen;
u8 flags;
};
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
@ -1671,6 +1676,16 @@ struct nft_trans {
u8 put_net:1;
};
/**
* struct nft_trans_hook - nf_tables hook update in transaction
* @list: used internally
* @hook: struct nft_hook with the device hook
*/
struct nft_trans_hook {
struct list_head list;
struct nft_hook *hook;
};
/**
* struct nft_trans_binding - nf_tables object with binding support in transaction
* @nft_trans: base structure, MUST be first member
@ -1856,6 +1871,11 @@ struct nft_trans_gc {
struct rcu_head rcu;
};
static inline int nft_trans_gc_space(const struct nft_trans_gc *trans)
{
return NFT_TRANS_GC_BATCHCOUNT - trans->count;
}
static inline void nft_ctx_update(struct nft_ctx *ctx,
const struct nft_trans *trans)
{

View File

@ -159,5 +159,9 @@ enum ip_conntrack_expect_events {
#define NF_CT_EXPECT_INACTIVE 0x2
#define NF_CT_EXPECT_USERSPACE 0x4
#ifdef __KERNEL__
#define NF_CT_EXPECT_MASK (NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE | \
NF_CT_EXPECT_USERSPACE)
#endif
#endif /* _UAPI_NF_CONNTRACK_COMMON_H */

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.46.1.el10.x86_64,mailto:security@almalinux.org
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.47.1.el10.x86_64,mailto:security@almalinux.org

View File

@ -6896,6 +6896,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma)
int err = 0;
unsigned long pagenum;
guard(mutex)(&rb->aux_mutex);
/*
* We map this as a VM_PFNMAP VMA.
*

View File

@ -296,7 +296,11 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
goto free_skb;
}
neigh_hh_bridge(&neigh->hh, skb);
if (neigh_hh_bridge(&neigh->hh, skb)) {
neigh_release(neigh);
goto free_skb;
}
skb->dev = br_indev;
ret = br_handle_frame_finish(net, sk, skb);

View File

@ -39,7 +39,9 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
dev = xt_in(par);
break;
case NF_BR_PRE_ROUTING:
dev = br_port_get_rcu(xt_in(par))->br->dev;
dev = netdev_master_upper_dev_get_rcu(xt_in(par));
if (!dev) /* bridge port removed? */
return EBT_DROP;
break;
default:
dev = NULL;

View File

@ -24,12 +24,18 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (skb_ensure_writable(skb, 0))
return EBT_DROP;
if (xt_hooknum(par) != NF_BR_BROUTING)
/* rcu_read_lock()ed by nf_hook_thresh */
ether_addr_copy(eth_hdr(skb)->h_dest,
br_port_get_rcu(xt_in(par))->br->dev->dev_addr);
else
if (xt_hooknum(par) != NF_BR_BROUTING) {
const struct net_device *dev;
dev = netdev_master_upper_dev_get_rcu(xt_in(par));
if (!dev)
return EBT_DROP;
ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
} else {
ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr);
}
skb->pkt_type = PACKET_HOST;
return info->target;
}

View File

@ -79,7 +79,7 @@ static int ipt_nat_register_lookups(struct net *net)
while (i)
nf_nat_ipv4_unregister_fn(net, &ops[--i]);
kfree(ops);
kfree_rcu(ops, rcu);
return ret;
}
}
@ -100,7 +100,7 @@ static void ipt_nat_unregister_lookups(struct net *net)
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++)
nf_nat_ipv4_unregister_fn(net, &ops[i]);
kfree(ops);
kfree_rcu(ops, rcu);
}
static int iptable_nat_table_init(struct net *net)

View File

@ -555,6 +555,8 @@ static void __exit nf_nat_h323_fini(void)
nf_ct_helper_expectfn_unregister(&q931_nat);
nf_ct_helper_expectfn_unregister(&callforwarding_nat);
synchronize_rcu();
nf_ct_helper_expectfn_destroy(&q931_nat);
nf_ct_helper_expectfn_destroy(&callforwarding_nat);
}
/****************************************************************************/

View File

@ -127,7 +127,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
fl4.saddr = get_saddr(iph->daddr);
}
*dest = 0;
nft_fib_store_result(dest, priv, NULL);
if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
return;

View File

@ -168,6 +168,10 @@ static int hbh_mt6_check(const struct xt_mtchk_param *par)
pr_debug("unknown flags %X\n", optsinfo->invflags);
return -EINVAL;
}
if (optsinfo->optsnr > IP6T_OPTS_OPTSNR) {
pr_debug("too many supported opts specified\n");
return -EINVAL;
}
if (optsinfo->flags & IP6T_OPTS_NSTRICT) {
pr_debug("Not strict - not implemented");

View File

@ -157,6 +157,10 @@ static int rt_mt6_check(const struct xt_mtchk_param *par)
pr_debug("unknown flags %X\n", rtinfo->invflags);
return -EINVAL;
}
if (rtinfo->addrnr > IP6T_RT_HOPS) {
pr_debug("too many addresses specified\n");
return -EINVAL;
}
if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) &&
(!(rtinfo->flags & IP6T_RT_TYP) ||
(rtinfo->rt_type != 0) ||

View File

@ -81,7 +81,7 @@ static int ip6t_nat_register_lookups(struct net *net)
while (i)
nf_nat_ipv6_unregister_fn(net, &ops[--i]);
kfree(ops);
kfree_rcu(ops, rcu);
return ret;
}
}
@ -102,7 +102,7 @@ static void ip6t_nat_unregister_lookups(struct net *net)
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++)
nf_nat_ipv6_unregister_fn(net, &ops[i]);
kfree(ops);
kfree_rcu(ops, rcu);
}
static int ip6table_nat_table_init(struct net *net)

View File

@ -192,7 +192,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph);
*dest = 0;
nft_fib_store_result(dest, priv, NULL);
rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, pkt->skb,
lookup_flags);
if (rt->dst.error)

View File

@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del);
*
*/
ip_set_id_t
ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set)
{
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
rcu_read_lock();
for (i = 0; i < inst->ip_set_max; i++) {
s = rcu_dereference(inst->ip_set_list)[i];
if (s && STRNCMP(s->name, name)) {
if (s && nla_strcmp(name, s->name) == 0) {
__ip_set_get(s);
index = i;
*set = s;

View File

@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
ret = ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s);
if (e.id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
/* "Loop detection" */
@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_NAMEREF]) {
e.refid = ip_set_get_byname(map->net,
nla_data(tb[IPSET_ATTR_NAMEREF]),
tb[IPSET_ATTR_NAMEREF],
&s);
if (e.refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;

View File

@ -21,6 +21,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
unsigned int timeout)
{
const struct nf_conntrack_helper *helper;
struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_expect *exp;
struct iphdr *iph = ip_hdr(skb);
struct rtable *rt = skb_rtable(skb);
@ -58,6 +59,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
if (exp == NULL)
goto out;
exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
helper = rcu_dereference(help->helper);
@ -70,8 +72,12 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
exp->expectfn = NULL;
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
exp->helper = NULL;
rcu_assign_pointer(exp->helper, helper);
rcu_assign_pointer(exp->assign_helper, NULL);
write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
exp->zone = ct->zone;
#endif
nf_ct_expect_related(exp, 0);
nf_ct_expect_put(exp);

View File

@ -576,6 +576,13 @@ static void destroy_gre_conntrack(struct nf_conn *ct)
#endif
}
static void warn_on_keymap_list_leak(const struct net *net)
{
#ifdef CONFIG_NF_CT_PROTO_GRE
WARN_ON_ONCE(!list_empty(&net->ct.nf_ct_proto.gre.keymap_list));
#endif
}
void nf_ct_destroy(struct nf_conntrack *nfct)
{
struct nf_conn *ct = (struct nf_conn *)nfct;
@ -1819,14 +1826,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl));
if (exp) {
struct nf_conntrack_helper *assign_helper;
/* Welcome, Mr. Bond. We've been expecting you... */
__set_bit(IPS_EXPECTED_BIT, &ct->status);
/* exp->master safe, refcnt bumped in nf_ct_find_expectation */
ct->master = exp->master;
if (exp->helper) {
assign_helper = rcu_dereference(exp->assign_helper);
if (assign_helper) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
rcu_assign_pointer(help->helper, exp->helper);
rcu_assign_pointer(help->helper, assign_helper);
}
#ifdef CONFIG_NF_CONNTRACK_MARK
@ -2522,6 +2532,7 @@ i_see_dead_people:
}
list_for_each_entry(net, net_exit_list, exit_list) {
warn_on_keymap_list_leak(net);
nf_conntrack_ecache_pernet_fini(net);
nf_conntrack_expect_pernet_fini(net);
free_percpu(net->ct.stat);

View File

@ -113,8 +113,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
const struct net *net)
{
return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
net_eq(net, nf_ct_net(i->master)) &&
nf_ct_zone_equal_any(i->master, zone);
net_eq(net, read_pnet(&i->net)) &&
nf_ct_exp_zone_equal_any(i, zone);
}
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
@ -314,12 +314,20 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
}
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
/* This function can only be used from packet path, where accessing
* master's helper is safe, because the packet holds a reference on
* the conntrack object. Never use it from control plane.
*/
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
u_int8_t family,
const union nf_inet_addr *saddr,
const union nf_inet_addr *daddr,
u_int8_t proto, const __be16 *src, const __be16 *dst)
{
struct nf_conntrack_helper *helper = NULL;
struct nf_conn *ct = exp->master;
struct net *net = read_pnet(&ct->ct_net);
struct nf_conn_help *help;
int len;
if (family == AF_INET)
@ -330,10 +338,22 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
exp->flags = 0;
exp->class = class;
exp->expectfn = NULL;
exp->helper = NULL;
help = nfct_help(ct);
if (help)
helper = rcu_dereference(help->helper);
rcu_assign_pointer(exp->helper, helper);
rcu_assign_pointer(exp->assign_helper, NULL);
write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
exp->zone = ct->zone;
#endif
exp->tuple.src.l3num = family;
exp->tuple.dst.protonum = proto;
exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
if (saddr) {
memcpy(&exp->tuple.src.u3, saddr, len);
if (sizeof(exp->tuple.src.u3) > len)
@ -662,7 +682,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
if (expect->flags & NF_CT_EXPECT_USERSPACE)
seq_printf(s, "%sUSERSPACE", delim);
helper = rcu_dereference(nfct_help(expect->master)->helper);
helper = rcu_dereference(expect->helper);
if (helper) {
seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
if (helper->expect_policy[expect->class].name[0])

View File

@ -331,6 +331,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
if (nf_h323_error_boundary(bs, 0, 2))
return H323_ERROR_BOUND;
len = get_bits(bs, 2) + 1;
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
BYTE_ALIGN(bs);
if (base && (f->attr & DECODE)) { /* timeToLive */
unsigned int v = get_uint(bs, len) + f->lb;
@ -796,7 +798,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
if (ext || (son->attr & OPEN)) {
BYTE_ALIGN(bs);
if (nf_h323_error_boundary(bs, len, 0))
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
if (nf_h323_error_boundary(bs, len, 0))

View File

@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
exp->helper = &nf_conntrack_helper_h245;
rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->helper = nf_conntrack_helper_q931;
rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
exp->helper = nf_conntrack_helper_q931;
rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
nathook = rcu_dereference(nfct_h323_nat_hook);
@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_UDP, NULL, &port);
exp->helper = nf_conntrack_helper_ras;
rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect RAS ");
@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->helper = nf_conntrack_helper_q931;
rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->helper = nf_conntrack_helper_q931;
rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");

View File

@ -283,6 +283,25 @@ void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
static bool expect_iter_expectfn(struct nf_conntrack_expect *exp, void *data)
{
const struct nf_ct_helper_expectfn *n = data;
/* Relies on registered expectfn descriptors having unique ->expectfn
* pointers, which holds for the in-tree NAT helpers.
*/
return exp->expectfn == n->expectfn;
}
/* Destroy expectations still pointing at @n->expectfn; call after the
* caller's RCU grace period so none outlives the (often modular) callback.
*/
void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n)
{
nf_ct_expect_iterate_destroy(expect_iter_expectfn, (void *)n);
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_destroy);
/* Caller should hold the rcu lock */
struct nf_ct_helper_expectfn *
nf_ct_helper_expectfn_find_by_name(const char *name)
@ -321,8 +340,8 @@ __printf(3, 4)
void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
const char *fmt, ...)
{
const char *helper_name = "(null)";
const struct nf_conn_help *help;
const struct nf_conntrack_helper *helper;
struct va_format vaf;
va_list args;
@ -331,14 +350,17 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
vaf.fmt = fmt;
vaf.va = &args;
/* Called from the helper function, this call never fails */
help = nfct_help(ct);
if (help) {
const struct nf_conntrack_helper *helper;
/* rcu_read_lock()ed by nf_hook_thresh */
helper = rcu_dereference(help->helper);
helper = rcu_dereference(help->helper);
if (helper)
helper_name = helper->name;
}
nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
"nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
"helper %s dropping packet: %pV ", helper_name, &vaf);
va_end(args);
}
@ -395,14 +417,15 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
{
struct nf_conn_help *help = nfct_help(exp->master);
const struct nf_conntrack_helper *me = data;
const struct nf_conntrack_helper *this;
if (exp->helper == me)
this = rcu_dereference_protected(exp->helper,
lockdep_is_held(&nf_conntrack_expect_lock));
if (this == me)
return true;
this = rcu_dereference_protected(help->helper,
this = rcu_dereference_protected(exp->assign_helper,
lockdep_is_held(&nf_conntrack_expect_lock));
return this == me;
}
@ -421,6 +444,11 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
nf_ct_expect_iterate_destroy(expect_iter_me, me);
nf_ct_iterate_destroy(unhelp, me);
/* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as
* last step, this ensures rcu readers of exp->helper are done.
* No need for another synchronize_rcu() here.
*/
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);

View File

@ -208,7 +208,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
if (parse_dcc(data, data_limit, &dcc_ip,
&dcc_port, &addr_beg_p, &addr_end_p)) {
pr_debug("unable to parse dcc command\n");
continue;
goto out;
}
pr_debug("DCC bound ip/port: %pI4:%u\n",
@ -222,7 +222,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
&tuple->src.u3.ip,
&dcc_ip, dcc_port);
continue;
goto out;
}
exp = nf_ct_expect_alloc(ct);

View File

@ -909,8 +909,8 @@ struct ctnetlink_filter {
};
static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = {
[CTA_FILTER_ORIG_FLAGS] = { .type = NLA_U32 },
[CTA_FILTER_REPLY_FLAGS] = { .type = NLA_U32 },
[CTA_FILTER_ORIG_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
[CTA_FILTER_REPLY_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
};
static int ctnetlink_parse_filter(const struct nlattr *attr,
@ -924,17 +924,11 @@ static int ctnetlink_parse_filter(const struct nlattr *attr,
if (ret)
return ret;
if (tb[CTA_FILTER_ORIG_FLAGS]) {
if (tb[CTA_FILTER_ORIG_FLAGS])
filter->orig_flags = nla_get_u32(tb[CTA_FILTER_ORIG_FLAGS]);
if (filter->orig_flags & ~CTA_FILTER_F_ALL)
return -EOPNOTSUPP;
}
if (tb[CTA_FILTER_REPLY_FLAGS]) {
if (tb[CTA_FILTER_REPLY_FLAGS])
filter->reply_flags = nla_get_u32(tb[CTA_FILTER_REPLY_FLAGS]);
if (filter->reply_flags & ~CTA_FILTER_F_ALL)
return -EOPNOTSUPP;
}
return 0;
}
@ -2653,7 +2647,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
[CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING,
.len = NF_CT_HELPER_NAME_LEN - 1 },
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
[CTA_EXPECT_FLAGS] = { .type = NLA_U32 },
[CTA_EXPECT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_CT_EXPECT_MASK),
[CTA_EXPECT_CLASS] = { .type = NLA_U32 },
[CTA_EXPECT_NAT] = { .type = NLA_NESTED },
[CTA_EXPECT_FN] = { .type = NLA_NUL_STRING },
@ -2661,7 +2655,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
static struct nf_conntrack_expect *
ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
struct nf_conntrack_helper *helper,
const struct nf_conntrack_helper *assign_helper,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask);
@ -2888,9 +2882,9 @@ static int
ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
u32 portid, u32 report)
{
struct nf_conntrack_helper *assign_helper = NULL;
struct nlattr *cda[CTA_EXPECT_MAX+1];
struct nf_conntrack_tuple tuple, mask;
struct nf_conntrack_helper *helper = NULL;
struct nf_conntrack_expect *exp;
int err;
@ -2899,6 +2893,9 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
if (err < 0)
return err;
if (!cda[CTA_EXPECT_TUPLE] || !cda[CTA_EXPECT_MASK])
return -EINVAL;
err = ctnetlink_glue_exp_parse((const struct nlattr * const *)cda,
ct, &tuple, &mask);
if (err < 0)
@ -2907,14 +2904,15 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
if (cda[CTA_EXPECT_HELP_NAME]) {
const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
nf_ct_protonum(ct));
if (helper == NULL)
assign_helper = __nf_conntrack_helper_find(helpname,
nf_ct_l3num(ct),
tuple.dst.protonum);
if (!assign_helper)
return -EOPNOTSUPP;
}
exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct,
helper, &tuple, &mask);
assign_helper, &tuple, &mask);
if (IS_ERR(exp))
return PTR_ERR(exp);
@ -3029,9 +3027,8 @@ static int
ctnetlink_exp_dump_expect(struct sk_buff *skb,
const struct nf_conntrack_expect *exp)
{
struct nf_conn *master = exp->master;
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
struct nf_conn_help *help;
struct nf_conntrack_helper *helper;
#if IS_ENABLED(CONFIG_NF_NAT)
struct nlattr *nest_parms;
struct nf_conntrack_tuple nat_tuple = {};
@ -3045,9 +3042,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
goto nla_put_failure;
if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
goto nla_put_failure;
if (ctnetlink_exp_dump_tuple(skb,
&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
CTA_EXPECT_MASTER) < 0)
if (ctnetlink_exp_dump_tuple(skb, &exp->master_tuple, CTA_EXPECT_MASTER) < 0)
goto nla_put_failure;
#if IS_ENABLED(CONFIG_NF_NAT)
@ -3060,9 +3055,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
if (nla_put_be32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir)))
goto nla_put_failure;
nat_tuple.src.l3num = nf_ct_l3num(master);
nat_tuple.src.l3num = exp->master_tuple.src.l3num;
nat_tuple.src.u3 = exp->saved_addr;
nat_tuple.dst.protonum = nf_ct_protonum(master);
nat_tuple.dst.protonum = exp->master_tuple.dst.protonum;
nat_tuple.src.u = exp->saved_proto;
if (ctnetlink_exp_dump_tuple(skb, &nat_tuple,
@ -3076,15 +3071,12 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
nla_put_be32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)) ||
nla_put_be32(skb, CTA_EXPECT_CLASS, htonl(exp->class)))
goto nla_put_failure;
help = nfct_help(master);
if (help) {
struct nf_conntrack_helper *helper;
helper = rcu_dereference(help->helper);
if (helper &&
nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
goto nla_put_failure;
}
helper = rcu_dereference(exp->helper);
if (helper &&
nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
goto nla_put_failure;
expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn);
if (expfn != NULL &&
nla_put_string(skb, CTA_EXPECT_FN, expfn->name))
@ -3199,7 +3191,7 @@ restart:
if (l3proto && exp->tuple.src.l3num != l3proto)
continue;
if (!net_eq(nf_ct_net(exp->master), net))
if (!net_eq(nf_ct_exp_net(exp), net))
continue;
if (cb->args[1]) {
@ -3231,7 +3223,7 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct nf_conn *ct = cb->data;
struct nf_conn_help *help = nfct_help(ct);
struct nf_conn_help *help;
u_int8_t l3proto = nfmsg->nfgen_family;
unsigned long last_id = cb->args[1];
struct nf_conntrack_expect *exp;
@ -3239,6 +3231,10 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
if (cb->args[0])
return 0;
help = nfct_help(ct);
if (!help)
return 0;
rcu_read_lock();
restart:
@ -3268,6 +3264,24 @@ out:
return skb->len;
}
static int ctnetlink_dump_exp_ct_start(struct netlink_callback *cb)
{
struct nf_conn *ct = cb->data;
if (!refcount_inc_not_zero(&ct->ct_general.use))
return -ENOENT;
return 0;
}
static int ctnetlink_dump_exp_ct_done(struct netlink_callback *cb)
{
struct nf_conn *ct = cb->data;
if (ct)
nf_ct_put(ct);
return 0;
}
static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
@ -3283,6 +3297,8 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct nf_conntrack_zone zone;
struct netlink_dump_control c = {
.dump = ctnetlink_exp_ct_dump_table,
.start = ctnetlink_dump_exp_ct_start,
.done = ctnetlink_dump_exp_ct_done,
};
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
@ -3395,12 +3411,9 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
static bool expect_iter_name(struct nf_conntrack_expect *exp, void *data)
{
struct nf_conntrack_helper *helper;
const struct nf_conn_help *m_help;
const char *name = data;
m_help = nfct_help(exp->master);
helper = rcu_dereference(m_help->helper);
helper = rcu_dereference(exp->helper);
if (!helper)
return false;
@ -3494,7 +3507,7 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
#if IS_ENABLED(CONFIG_NF_NAT)
static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = {
[CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 },
[CTA_EXPECT_NAT_DIR] = NLA_POLICY_MAX(NLA_BE32, IP_CT_DIR_REPLY),
[CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
};
#endif
@ -3535,20 +3548,26 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
static struct nf_conntrack_expect *
ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
struct nf_conntrack_helper *helper,
const struct nf_conntrack_helper *assign_helper,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask)
{
u_int32_t class = 0;
struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_helper *helper;
struct nf_conntrack_expect *exp;
struct nf_conn_help *help;
u32 class = 0;
int err;
help = nfct_help(ct);
if (!help)
return ERR_PTR(-EOPNOTSUPP);
if (cda[CTA_EXPECT_CLASS] && helper) {
helper = rcu_dereference(help->helper);
if (!helper)
return ERR_PTR(-EOPNOTSUPP);
if (cda[CTA_EXPECT_CLASS]) {
class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS]));
if (class > helper->expect_class_max)
return ERR_PTR(-EINVAL);
@ -3578,7 +3597,13 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->class = class;
exp->master = ct;
exp->helper = helper;
write_pnet(&exp->net, net);
#ifdef CONFIG_NF_CONNTRACK_ZONES
exp->zone = ct->zone;
#endif
rcu_assign_pointer(exp->helper, helper);
rcu_assign_pointer(exp->assign_helper, assign_helper);
exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
exp->tuple = *tuple;
exp->mask.src.u3 = mask->src.u3;
exp->mask.src.u.all = mask->src.u.all;
@ -3588,6 +3613,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp, nf_ct_l3num(ct));
if (err < 0)
goto err_out;
#if IS_ENABLED(CONFIG_NF_NAT)
} else {
memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
exp->dir = 0;
#endif
}
return exp;
err_out:
@ -3603,7 +3634,6 @@ ctnetlink_create_expect(struct net *net,
{
struct nf_conntrack_tuple tuple, mask, master_tuple;
struct nf_conntrack_tuple_hash *h = NULL;
struct nf_conntrack_helper *helper = NULL;
struct nf_conntrack_expect *exp;
struct nf_conn *ct;
int err;
@ -3629,33 +3659,7 @@ ctnetlink_create_expect(struct net *net,
ct = nf_ct_tuplehash_to_ctrack(h);
rcu_read_lock();
if (cda[CTA_EXPECT_HELP_NAME]) {
const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
helper = __nf_conntrack_helper_find(helpname, u3,
nf_ct_protonum(ct));
if (helper == NULL) {
rcu_read_unlock();
#ifdef CONFIG_MODULES
if (request_module("nfct-helper-%s", helpname) < 0) {
err = -EOPNOTSUPP;
goto err_ct;
}
rcu_read_lock();
helper = __nf_conntrack_helper_find(helpname, u3,
nf_ct_protonum(ct));
if (helper) {
err = -EAGAIN;
goto err_rcu;
}
rcu_read_unlock();
#endif
err = -EOPNOTSUPP;
goto err_ct;
}
}
exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask);
exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask);
if (IS_ERR(exp)) {
err = PTR_ERR(exp);
goto err_rcu;
@ -3665,8 +3669,8 @@ ctnetlink_create_expect(struct net *net,
nf_ct_expect_put(exp);
err_rcu:
rcu_read_unlock();
err_ct:
nf_ct_put(ct);
return err;
}

View File

@ -225,13 +225,9 @@ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid)
if (nf_ct_expect_related(exp_reply, 0) != 0)
goto out_unexpect_orig;
/* Add GRE keymap entries */
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0)
if (!nf_ct_gre_keymap_add(ct, &exp_orig->tuple,
&exp_reply->tuple))
goto out_unexpect_both;
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) {
nf_ct_gre_keymap_destroy(ct);
goto out_unexpect_both;
}
ret = 0;
out_put_both:

View File

@ -85,41 +85,97 @@ static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
return key;
}
/* add a single keymap entry, associate with specified master ct */
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
struct nf_conntrack_tuple *t)
enum nf_ct_gre_km_act {
NF_CT_GRE_KM_NEW,
NF_CT_GRE_KM_BAD,
NF_CT_GRE_KM_DUP
};
static enum nf_ct_gre_km_act
nf_ct_gre_km_acceptable(const struct nf_ct_pptp_master *ct_pptp_info,
const struct nf_conntrack_tuple *orig,
const struct nf_conntrack_tuple *repl)
{
struct nf_ct_gre_keymap *km_orig, *km_repl;
lockdep_assert_held(&keymap_lock);
km_orig = ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL];
km_repl = ct_pptp_info->keymap[IP_CT_DIR_REPLY];
if (km_orig && km_repl) {
if (!gre_key_cmpfn(km_orig, orig))
return NF_CT_GRE_KM_BAD;
if (!gre_key_cmpfn(km_repl, repl))
return NF_CT_GRE_KM_BAD;
return NF_CT_GRE_KM_DUP;
}
DEBUG_NET_WARN_ON_ONCE(km_orig);
DEBUG_NET_WARN_ON_ONCE(km_repl);
return NF_CT_GRE_KM_NEW;
}
/* add keymap entries, associate with specified master ct */
bool nf_ct_gre_keymap_add(struct nf_conn *ct,
const struct nf_conntrack_tuple *orig,
const struct nf_conntrack_tuple *repl)
{
struct net *net = nf_ct_net(ct);
struct nf_gre_net *net_gre = gre_pernet(net);
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
struct nf_ct_gre_keymap **kmp, *km;
struct nf_ct_gre_keymap *km_orig, *km_repl;
bool ret = false;
kmp = &ct_pptp_info->keymap[dir];
if (*kmp) {
/* check whether it's a retransmission */
list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
if (gre_key_cmpfn(km, t) && km == *kmp)
return 0;
}
pr_debug("trying to override keymap_%s for ct %p\n",
dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
return -EEXIST;
}
km_orig = kmalloc(sizeof(*km_orig), GFP_ATOMIC);
if (!km_orig)
return false;
km_repl = kmalloc(sizeof(*km_repl), GFP_ATOMIC);
if (!km_repl)
goto km_free;
km = kmalloc(sizeof(*km), GFP_ATOMIC);
if (!km)
return -ENOMEM;
memcpy(&km->tuple, t, sizeof(*t));
*kmp = km;
pr_debug("adding new entry %p: ", km);
nf_ct_dump_tuple(&km->tuple);
memcpy(&km_orig->tuple, orig, sizeof(*orig));
memcpy(&km_repl->tuple, repl, sizeof(*repl));
spin_lock_bh(&keymap_lock);
list_add_tail(&km->list, &net_gre->keymap_list);
if (nf_ct_is_dying(ct))
goto unlock_free;
switch (nf_ct_gre_km_acceptable(ct_pptp_info, orig, repl)) {
case NF_CT_GRE_KM_NEW:
break;
case NF_CT_GRE_KM_DUP:
ret = true;
goto unlock_free;
case NF_CT_GRE_KM_BAD:
pr_debug("trying to override keymap for ct %p\n", ct);
goto unlock_free;
}
if (ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] ||
ct_pptp_info->keymap[IP_CT_DIR_REPLY])
goto unlock_free;
pr_debug("adding new entries %p,%p: ", km_orig, km_repl);
nf_ct_dump_tuple(&km_orig->tuple);
nf_ct_dump_tuple(&km_repl->tuple);
list_add_tail_rcu(&km_orig->list, &net_gre->keymap_list);
list_add_tail_rcu(&km_repl->list, &net_gre->keymap_list);
ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] = km_orig;
ct_pptp_info->keymap[IP_CT_DIR_REPLY] = km_repl;
spin_unlock_bh(&keymap_lock);
return 0;
return true;
unlock_free:
spin_unlock_bh(&keymap_lock);
km_free:
kfree(km_orig);
kfree(km_repl);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);

View File

@ -587,7 +587,8 @@ nla_put_failure:
}
static const struct nla_policy sctp_nla_policy[CTA_PROTOINFO_SCTP_MAX+1] = {
[CTA_PROTOINFO_SCTP_STATE] = { .type = NLA_U8 },
[CTA_PROTOINFO_SCTP_STATE] = NLA_POLICY_MAX(NLA_U8,
SCTP_CONNTRACK_HEARTBEAT_SENT),
[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] = { .type = NLA_U32 },
[CTA_PROTOINFO_SCTP_VTAG_REPLY] = { .type = NLA_U32 },
};

View File

@ -1221,7 +1221,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
new_state = old_state;
}
if (((test_bit(IPS_SEEN_REPLY_BIT, &ct->status)
&& ct->proto.tcp.last_index == TCP_SYN_SET)
&& ct->proto.tcp.last_index == TCP_SYN_SET
&& ct->proto.tcp.last_dir != dir)
|| (!test_bit(IPS_ASSURED_BIT, &ct->status)
&& ct->proto.tcp.last_index == TCP_ACK_SET))
&& ntohl(th->ack_seq) == ct->proto.tcp.last_end) {
@ -1385,9 +1386,9 @@ nla_put_failure:
}
static const struct nla_policy tcp_nla_policy[CTA_PROTOINFO_TCP_MAX+1] = {
[CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 },
[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 },
[CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 },
[CTA_PROTOINFO_TCP_STATE] = NLA_POLICY_MAX(NLA_U8, TCP_CONNTRACK_SYN_SENT2),
[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
[CTA_PROTOINFO_TCP_WSCALE_REPLY] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .len = sizeof(struct nf_ct_tcp_flags) },
[CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .len = sizeof(struct nf_ct_tcp_flags) },
};
@ -1414,10 +1415,6 @@ static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct)
if (err < 0)
return err;
if (tb[CTA_PROTOINFO_TCP_STATE] &&
nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]) >= TCP_CONNTRACK_MAX)
return -EINVAL;
spin_lock_bh(&ct->lock);
if (tb[CTA_PROTOINFO_TCP_STATE])
ct->proto.tcp.state = nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]);

View File

@ -181,6 +181,57 @@ static int sip_parse_addr(const struct nf_conn *ct, const char *cp,
return 1;
}
/* Parse optional port number after IP address.
* Returns false on malformed input, true otherwise.
* If port is non-NULL, stores parsed port in network byte order.
* If no port is present, sets *port to default SIP port.
*/
static bool sip_parse_port(const char *dptr, const char **endp,
const char *limit, __be16 *port)
{
unsigned int p = 0;
int len = 0;
if (dptr >= limit)
return false;
if (*dptr != ':') {
if (port)
*port = htons(SIP_PORT);
if (endp)
*endp = dptr;
return true;
}
dptr++; /* skip ':' */
while (dptr < limit && isdigit(*dptr)) {
p = p * 10 + (*dptr - '0');
dptr++;
len++;
if (len > 5) /* max "65535" */
return false;
}
if (len == 0)
return false;
/* reached limit while parsing port */
if (dptr >= limit)
return false;
if (p < 1024 || p > 65535)
return false;
if (port)
*port = htons(p);
if (endp)
*endp = dptr;
return true;
}
/* skip ip address. returns its length. */
static int epaddr_len(const struct nf_conn *ct, const char *dptr,
const char *limit, int *shift)
@ -193,11 +244,8 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr,
return 0;
}
/* Port number */
if (*dptr == ':') {
dptr++;
dptr += digits_len(ct, dptr, limit, shift);
}
if (!sip_parse_port(dptr, &dptr, limit, NULL))
return 0;
return dptr - aux;
}
@ -228,6 +276,51 @@ static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
return epaddr_len(ct, dptr, limit, shift);
}
/* simple_strtoul stops after first non-number character.
* But as we're not dealing with c-strings, we can't rely on
* hitting \r,\n,\0 etc. before moving past end of buffer.
*
* This is a variant of simple_strtoul, but doesn't require
* a c-string.
*
* If value exceeds UINT_MAX, 0 is returned.
*/
static unsigned int sip_strtouint(const char *cp, unsigned int len, char **endp)
{
const unsigned int max = sizeof("4294967295");
unsigned int olen = len;
const char *s = cp;
u64 result = 0;
if (len > max)
len = max;
while (olen > 0 && isdigit(*s)) {
unsigned int value;
if (len == 0)
goto err;
value = *s - '0';
result = result * 10 + value;
if (result > UINT_MAX)
goto err;
s++;
len--;
olen--;
}
if (endp)
*endp = (char *)s;
return result;
err:
if (endp)
*endp = (char *)cp;
return 0;
}
/* Parse a SIP request line of the form:
*
* Request-Line = Method SP Request-URI SP SIP-Version CRLF
@ -241,7 +334,6 @@ int ct_sip_parse_request(const struct nf_conn *ct,
{
const char *start = dptr, *limit = dptr + datalen, *end;
unsigned int mlen;
unsigned int p;
int shift = 0;
/* Skip method and following whitespace */
@ -267,14 +359,8 @@ int ct_sip_parse_request(const struct nf_conn *ct,
if (!sip_parse_addr(ct, dptr, &end, addr, limit, true))
return -1;
if (end < limit && *end == ':') {
end++;
p = simple_strtoul(end, (char **)&end, 10);
if (p < 1024 || p > 65535)
return -1;
*port = htons(p);
} else
*port = htons(SIP_PORT);
if (!sip_parse_port(end, &end, limit, port))
return -1;
if (end == dptr)
return 0;
@ -509,7 +595,6 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
union nf_inet_addr *addr, __be16 *port)
{
const char *c, *limit = dptr + datalen;
unsigned int p;
int ret;
ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
@ -520,14 +605,8 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true))
return -1;
if (*c == ':') {
c++;
p = simple_strtoul(c, (char **)&c, 10);
if (p < 1024 || p > 65535)
return -1;
*port = htons(p);
} else
*port = htons(SIP_PORT);
if (!sip_parse_port(c, &c, limit, port))
return -1;
if (dataoff)
*dataoff = c - dptr;
@ -609,7 +688,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
return 0;
start += strlen(name);
*val = simple_strtoul(start, &end, 0);
*val = sip_strtouint(start, limit - start, (char **)&end);
if (start == end)
return -1;
if (matchoff && matchlen) {
@ -924,7 +1003,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple);
if (!exp || exp->master == ct ||
nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
exp->helper != nfct_help(ct)->helper ||
exp->class != class)
break;
#if IS_ENABLED(CONFIG_NF_NAT)
@ -1061,6 +1140,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
mediaoff = sdpoff;
for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) {
char *end;
if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen,
SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
&mediaoff, &medialen) <= 0)
@ -1076,8 +1157,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
mediaoff += t->len;
medialen -= t->len;
port = simple_strtoul(*dptr + mediaoff, NULL, 10);
if (port == 0)
port = sip_strtouint(*dptr + mediaoff, *datalen - mediaoff, (char **)&end);
if (port == 0 || *dptr + mediaoff == end)
continue;
if (port < 1024 || port > 65535) {
nf_ct_helper_log(skb, ct, "wrong port %u", port);
@ -1249,7 +1330,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
*/
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
&matchoff, &matchlen) > 0)
expires = simple_strtoul(*dptr + matchoff, NULL, 10);
expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL);
ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
SIP_HDR_CONTACT, NULL,
@ -1280,6 +1361,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
goto store_cseq;
}
helper = rcu_dereference(nfct_help(ct)->helper);
if (!helper)
return NF_DROP;
exp = nf_ct_expect_alloc(ct);
if (!exp) {
nf_ct_helper_log(skb, ct, "cannot alloc expectation");
@ -1290,14 +1375,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
if (sip_direct_signalling)
saddr = &ct->tuplehash[!dir].tuple.src.u3;
helper = rcu_dereference(nfct_help(ct)->helper);
if (!helper)
return NF_DROP;
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
saddr, &daddr, proto, NULL, &port);
exp->timeout.expires = sip_timeout * HZ;
exp->helper = helper;
rcu_assign_pointer(exp->assign_helper, helper);
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
hooks = rcu_dereference(nf_nat_sip_hooks);
@ -1353,7 +1434,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff,
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
&matchoff, &matchlen) > 0)
expires = simple_strtoul(*dptr + matchoff, NULL, 10);
expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL);
while (1) {
unsigned int c_expires = expires;
@ -1413,10 +1494,12 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
unsigned int matchoff, matchlen, matchend;
unsigned int code, cseq, i;
char *end;
if (*datalen < strlen("SIP/2.0 200"))
return NF_ACCEPT;
code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
code = sip_strtouint(*dptr + strlen("SIP/2.0 "),
*datalen - strlen("SIP/2.0 "), NULL);
if (!code) {
nf_ct_helper_log(skb, ct, "cannot get code");
return NF_DROP;
@ -1427,8 +1510,8 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
nf_ct_helper_log(skb, ct, "cannot parse cseq");
return NF_DROP;
}
cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
if (!cseq && *(*dptr + matchoff) != '0') {
cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end);
if (*dptr + matchoff == end) {
nf_ct_helper_log(skb, ct, "cannot get cseq");
return NF_DROP;
}
@ -1477,6 +1560,7 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
const struct sip_handler *handler;
char *end;
handler = &sip_handlers[i];
if (handler->request == NULL)
@ -1493,8 +1577,8 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
nf_ct_helper_log(skb, ct, "cannot parse cseq");
return NF_DROP;
}
cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
if (!cseq && *(*dptr + matchoff) != '0') {
cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end);
if (*dptr + matchoff == end) {
nf_ct_helper_log(skb, ct, "cannot get cseq");
return NF_DROP;
}
@ -1534,11 +1618,12 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
{
struct tcphdr *th, _tcph;
unsigned int dataoff, datalen;
unsigned int matchoff, matchlen, clen;
unsigned int matchoff, matchlen;
unsigned int msglen, origlen;
const char *dptr, *end;
s16 diff, tdiff = 0;
int ret = NF_ACCEPT;
unsigned long clen;
bool term;
if (ctinfo != IP_CT_ESTABLISHED &&
@ -1569,10 +1654,13 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
&matchoff, &matchlen) <= 0)
break;
clen = simple_strtoul(dptr + matchoff, (char **)&end, 10);
clen = sip_strtouint(dptr + matchoff, datalen - matchoff, (char **)&end);
if (dptr + matchoff == end)
break;
if (clen > datalen)
break;
term = false;
for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) {
if (end[0] == '\r' && end[1] == '\n' &&

View File

@ -17,12 +17,24 @@
static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
static bool nf_dev_xmit_recursion(void)
{
return unlikely(__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT);
}
static void nf_dev_xmit_recursion_inc(void)
{
__this_cpu_inc(nf_dup_skb_recursion);
}
static void nf_dev_xmit_recursion_dec(void)
{
__this_cpu_dec(nf_dup_skb_recursion);
}
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
enum nf_dev_hooks hook)
{
if (__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT)
goto err;
if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) {
if (skb_cow_head(skb, skb->mac_len))
goto err;
@ -32,9 +44,15 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
skb->dev = dev;
skb_clear_tstamp(skb);
__this_cpu_inc(nf_dup_skb_recursion);
local_bh_disable();
if (nf_dev_xmit_recursion()) {
local_bh_enable();
goto err;
}
nf_dev_xmit_recursion_inc();
dev_queue_xmit(skb);
__this_cpu_dec(nf_dup_skb_recursion);
nf_dev_xmit_recursion_dec();
local_bh_enable();
return;
err:
kfree_skb(skb);

View File

@ -394,7 +394,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
return 0;
}
if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
return -1;
flow_offload_refresh(flow_table, flow, false);
@ -673,7 +673,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
return 0;
}
if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
return -1;
flow_offload_refresh(flow_table, flow, false);

View File

@ -799,8 +799,8 @@ static void dump_mac_header(struct nf_log_buf *m,
fallback:
nf_log_buf_add(m, "MAC=");
if (dev->hard_header_len &&
skb->mac_header != skb->network_header) {
if (dev->hard_header_len && skb_mac_header_was_set(skb) &&
skb_mac_header_len(skb) != 0) {
const unsigned char *p = skb_mac_header(skb);
unsigned int i;

View File

@ -50,7 +50,7 @@ static unsigned int help(struct sk_buff *skb,
return NF_DROP;
}
sprintf(buffer, "%u", port);
snprintf(buffer, sizeof(buffer), "%u", port);
if (!nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
protoff, matchoff, matchlen,
buffer, strlen(buffer))) {

View File

@ -1236,9 +1236,11 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
ret = nf_register_net_hooks(net, nat_ops, ops_count);
if (ret < 0) {
mutex_unlock(&nf_nat_proto_mutex);
for (i = 0; i < ops_count; i++)
kfree(nat_ops[i].priv);
kfree(nat_ops);
for (i = 0; i < ops_count; i++) {
priv = nat_ops[i].priv;
kfree_rcu(priv, rcu_head);
}
kfree_rcu(nat_ops, rcu);
return ret;
}
@ -1302,7 +1304,7 @@ void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
}
nat_proto_net->nat_hook_ops = NULL;
kfree(nat_ops);
kfree_rcu(nat_ops, rcu);
}
unlock:
mutex_unlock(&nf_nat_proto_mutex);
@ -1353,6 +1355,7 @@ static int __init nf_nat_init(void)
RCU_INIT_POINTER(nf_nat_hook, NULL);
nf_ct_helper_expectfn_unregister(&follow_master_nat);
synchronize_net();
nf_ct_helper_expectfn_destroy(&follow_master_nat);
unregister_pernet_subsys(&nat_net_ops);
kvfree(nf_nat_bysource);
}
@ -1370,6 +1373,7 @@ static void __exit nf_nat_cleanup(void)
RCU_INIT_POINTER(nf_nat_hook, NULL);
synchronize_net();
nf_ct_helper_expectfn_destroy(&follow_master_nat);
kvfree(nf_nat_bysource);
unregister_pernet_subsys(&nat_net_ops);
}

View File

@ -68,25 +68,27 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff,
}
static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer,
size_t size,
const union nf_inet_addr *addr, bool delim)
{
if (nf_ct_l3num(ct) == NFPROTO_IPV4)
return sprintf(buffer, "%pI4", &addr->ip);
return scnprintf(buffer, size, "%pI4", &addr->ip);
else {
if (delim)
return sprintf(buffer, "[%pI6c]", &addr->ip6);
return scnprintf(buffer, size, "[%pI6c]", &addr->ip6);
else
return sprintf(buffer, "%pI6c", &addr->ip6);
return scnprintf(buffer, size, "%pI6c", &addr->ip6);
}
}
static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer,
size_t size,
const union nf_inet_addr *addr, u16 port)
{
if (nf_ct_l3num(ct) == NFPROTO_IPV4)
return sprintf(buffer, "%pI4:%u", &addr->ip, port);
return scnprintf(buffer, size, "%pI4:%u", &addr->ip, port);
else
return sprintf(buffer, "[%pI6c]:%u", &addr->ip6, port);
return scnprintf(buffer, size, "[%pI6c]:%u", &addr->ip6, port);
}
static int map_addr(struct sk_buff *skb, unsigned int protoff,
@ -119,7 +121,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
if (nf_inet_addr_cmp(&newaddr, addr) && newport == port)
return 1;
buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport));
buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer), &newaddr, ntohs(newport));
return mangle_packet(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen, buffer, buflen);
}
@ -212,7 +214,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
&addr, true) > 0 &&
nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) &&
!nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) {
buflen = sip_sprintf_addr(ct, buffer,
buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer),
&ct->tuplehash[!dir].tuple.dst.u3,
true);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
@ -229,7 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
&addr, false) > 0 &&
nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) &&
!nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) {
buflen = sip_sprintf_addr(ct, buffer,
buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer),
&ct->tuplehash[!dir].tuple.src.u3,
false);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
@ -244,10 +246,11 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
"rport=", &poff, &plen,
&n) > 0 &&
n >= 1024 && n <= 65535 &&
htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
buflen = sprintf(buffer, "%u", ntohs(p));
buflen = scnprintf(buffer, sizeof(buffer), "%u", ntohs(p));
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
poff, plen, buffer, buflen)) {
nf_ct_helper_log(skb, ct, "cannot mangle rport");
@ -418,7 +421,8 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) ||
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port);
buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer),
&newaddr, port);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen, buffer, buflen)) {
nf_ct_helper_log(skb, ct, "cannot mangle packet");
@ -438,8 +442,8 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
char buffer[sizeof("4294967295")];
unsigned int matchoff, matchlen;
char buffer[sizeof("65536")];
int buflen, c_len;
/* Get actual SDP length */
@ -454,7 +458,7 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
&matchoff, &matchlen) <= 0)
return 0;
buflen = sprintf(buffer, "%u", c_len);
buflen = scnprintf(buffer, sizeof(buffer), "%u", c_len);
return mangle_packet(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen, buffer, buflen);
}
@ -491,7 +495,7 @@ static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff,
char buffer[INET6_ADDRSTRLEN];
unsigned int buflen;
buflen = sip_sprintf_addr(ct, buffer, addr, false);
buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false);
if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen,
sdpoff, type, term, buffer, buflen))
return 0;
@ -509,7 +513,7 @@ static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff,
char buffer[sizeof("nnnnn")];
unsigned int buflen;
buflen = sprintf(buffer, "%u", port);
buflen = scnprintf(buffer, sizeof(buffer), "%u", port);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen, buffer, buflen))
return 0;
@ -529,7 +533,7 @@ static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff
unsigned int buflen;
/* Mangle session description owner and contact addresses */
buflen = sip_sprintf_addr(ct, buffer, addr, false);
buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false);
if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
SDP_HDR_OWNER, SDP_HDR_MEDIA, buffer, buflen))
return 0;
@ -651,6 +655,7 @@ static void __exit nf_nat_sip_fini(void)
RCU_INIT_POINTER(nf_nat_sip_hooks, NULL);
nf_ct_helper_expectfn_unregister(&sip_nat);
synchronize_rcu();
nf_ct_helper_expectfn_destroy(&sip_nat);
}
static const struct nf_nat_sip_hooks sip_hooks = {

View File

@ -60,6 +60,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
struct nf_hook_state *state = &entry->state;
/* Release those devices we held, or Alexey will kill me. */
dev_put(entry->skb_dev);
dev_put(state->in);
dev_put(state->out);
if (state->sk)
@ -101,6 +102,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt))
return false;
dev_hold(entry->skb_dev);
dev_hold(state->in);
dev_hold(state->out);
@ -201,11 +203,11 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
*entry = (struct nf_queue_entry) {
.skb = skb,
.skb_dev = skb->dev,
.state = *state,
.hook_index = index,
.size = sizeof(*entry) + route_key_size,
};
__nf_queue_entry_init_physdevs(entry);
if (!nf_queue_entry_get_refs(entry)) {

View File

@ -21,6 +21,8 @@
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_synproxy.h>
static DEFINE_MUTEX(synproxy_mutex);
unsigned int synproxy_net_id;
EXPORT_SYMBOL_GPL(synproxy_net_id);
@ -199,6 +201,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
if (skb_ensure_writable(skb, optend))
return 0;
th = (struct tcphdr *)(skb->data + protoff);
while (optoff < optend) {
unsigned char *op = skb->data + optoff;
@ -766,26 +770,31 @@ static const struct nf_hook_ops ipv4_synproxy_ops[] = {
int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
{
int err;
int err = 0;
mutex_lock(&synproxy_mutex);
if (snet->hook_ref4 == 0) {
err = nf_register_net_hooks(net, ipv4_synproxy_ops,
ARRAY_SIZE(ipv4_synproxy_ops));
if (err)
return err;
goto out;
}
snet->hook_ref4++;
return 0;
out:
mutex_unlock(&synproxy_mutex);
return err;
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
{
mutex_lock(&synproxy_mutex);
snet->hook_ref4--;
if (snet->hook_ref4 == 0)
nf_unregister_net_hooks(net, ipv4_synproxy_ops,
ARRAY_SIZE(ipv4_synproxy_ops));
mutex_unlock(&synproxy_mutex);
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
@ -1190,27 +1199,32 @@ static const struct nf_hook_ops ipv6_synproxy_ops[] = {
int
nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
{
int err;
int err = 0;
mutex_lock(&synproxy_mutex);
if (snet->hook_ref6 == 0) {
err = nf_register_net_hooks(net, ipv6_synproxy_ops,
ARRAY_SIZE(ipv6_synproxy_ops));
if (err)
return err;
goto out;
}
snet->hook_ref6++;
return 0;
out:
mutex_unlock(&synproxy_mutex);
return err;
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
void
nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
{
mutex_lock(&synproxy_mutex);
snet->hook_ref6--;
if (snet->hook_ref6 == 0)
nf_unregister_net_hooks(net, ipv6_synproxy_ops,
ARRAY_SIZE(ipv6_synproxy_ops));
mutex_unlock(&synproxy_mutex);
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
#endif /* CONFIG_IPV6 */

View File

@ -373,7 +373,40 @@ static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu);
}
static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook)
{
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
static void nft_trans_hook_destroy(struct nft_trans_hook *trans_hook)
{
list_del(&trans_hook->list);
kfree(trans_hook);
}
static void nft_netdev_unregister_trans_hook(struct net *net,
const struct nft_table *table,
struct list_head *hook_list)
{
struct nft_trans_hook *trans_hook, *next;
struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry_safe(trans_hook, next, hook_list, list) {
hook = trans_hook->hook;
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
list_for_each_entry(ops, &hook->ops_list, list)
nf_unregister_net_hook(net, ops);
}
nft_netdev_hook_unlink_free_rcu(hook);
nft_trans_hook_destroy(trans_hook);
}
}
static void nft_netdev_unregister_hooks(struct net *net,
const struct nft_table *table,
struct list_head *hook_list,
bool release_netdev)
{
@ -381,12 +414,12 @@ static void nft_netdev_unregister_hooks(struct net *net,
struct nf_hook_ops *ops;
list_for_each_entry_safe(hook, next, hook_list, list) {
list_for_each_entry(ops, &hook->ops_list, list)
nf_unregister_net_hook(net, ops);
if (release_netdev) {
list_del(&hook->list);
nft_netdev_hook_free_rcu(hook);
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
list_for_each_entry(ops, &hook->ops_list, list)
nf_unregister_net_hook(net, ops);
}
if (release_netdev)
nft_netdev_hook_unlink_free_rcu(hook);
}
}
@ -421,20 +454,25 @@ static void __nf_tables_unregister_hook(struct net *net,
struct nft_base_chain *basechain;
const struct nf_hook_ops *ops;
if (table->flags & NFT_TABLE_F_DORMANT ||
!nft_is_base_chain(chain))
if (!nft_is_base_chain(chain))
return;
basechain = nft_base_chain(chain);
ops = &basechain->ops;
/* must also be called for dormant tables */
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
nft_netdev_unregister_hooks(net, table, &basechain->hook_list,
release_netdev);
return;
}
if (table->flags & NFT_TABLE_F_DORMANT)
return;
if (basechain->type->ops_unregister)
return basechain->type->ops_unregister(net, ops);
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
nft_netdev_unregister_hooks(net, &basechain->hook_list,
release_netdev);
else
nf_unregister_net_hook(net, &basechain->ops);
nf_unregister_net_hook(net, &basechain->ops);
}
static void nf_tables_unregister_hook(struct net *net,
@ -828,7 +866,6 @@ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx,
nft_set_elem_change_active(ctx->net, set, ext);
nft_setelem_data_deactivate(ctx->net, set, catchall->elem);
break;
}
}
@ -1988,15 +2025,69 @@ static int nft_nla_put_hook_dev(struct sk_buff *skb, struct nft_hook *hook)
return nla_put_string(skb, attr, hook->ifname);
}
struct nft_hook_dump_ctx {
struct nft_hook *first;
int n;
};
static int nft_dump_basechain_hook_one(struct sk_buff *skb,
struct nft_hook *hook,
struct nft_hook_dump_ctx *dump_ctx)
{
if (!dump_ctx->first)
dump_ctx->first = hook;
if (nft_nla_put_hook_dev(skb, hook))
return -1;
dump_ctx->n++;
return 0;
}
static int nft_dump_basechain_hook_list(struct sk_buff *skb,
const struct net *net,
const struct list_head *hook_list,
struct nft_hook_dump_ctx *dump_ctx)
{
struct nft_hook *hook;
int err;
list_for_each_entry_rcu(hook, hook_list, list,
lockdep_commit_lock_is_held(net)) {
err = nft_dump_basechain_hook_one(skb, hook, dump_ctx);
if (err < 0)
return err;
}
return 0;
}
static int nft_dump_basechain_trans_hook_list(struct sk_buff *skb,
const struct list_head *trans_hook_list,
struct nft_hook_dump_ctx *dump_ctx)
{
struct nft_trans_hook *trans_hook;
int err;
list_for_each_entry(trans_hook, trans_hook_list, list) {
err = nft_dump_basechain_hook_one(skb, trans_hook->hook, dump_ctx);
if (err < 0)
return err;
}
return 0;
}
static int nft_dump_basechain_hook(struct sk_buff *skb,
const struct net *net, int family,
const struct nft_base_chain *basechain,
const struct list_head *hook_list)
const struct list_head *hook_list,
const struct list_head *trans_hook_list)
{
const struct nf_hook_ops *ops = &basechain->ops;
struct nft_hook *hook, *first = NULL;
struct nft_hook_dump_ctx dump_hook_ctx = {};
struct nlattr *nest, *nest_devs;
int n = 0;
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
if (nest == NULL)
@ -2011,23 +2102,23 @@ static int nft_dump_basechain_hook(struct sk_buff *skb,
if (!nest_devs)
goto nla_put_failure;
if (!hook_list)
if (!hook_list && !trans_hook_list)
hook_list = &basechain->hook_list;
list_for_each_entry_rcu(hook, hook_list, list,
lockdep_commit_lock_is_held(net)) {
if (!first)
first = hook;
if (nft_nla_put_hook_dev(skb, hook))
goto nla_put_failure;
n++;
if (hook_list &&
nft_dump_basechain_hook_list(skb, net, hook_list, &dump_hook_ctx)) {
goto nla_put_failure;
} else if (trans_hook_list &&
nft_dump_basechain_trans_hook_list(skb, trans_hook_list,
&dump_hook_ctx)) {
goto nla_put_failure;
}
nla_nest_end(skb, nest_devs);
if (n == 1 &&
!hook_is_prefix(first) &&
nla_put_string(skb, NFTA_HOOK_DEV, first->ifname))
if (dump_hook_ctx.n == 1 &&
!hook_is_prefix(dump_hook_ctx.first) &&
nla_put_string(skb, NFTA_HOOK_DEV, dump_hook_ctx.first->ifname))
goto nla_put_failure;
}
nla_nest_end(skb, nest);
@ -2041,7 +2132,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
u32 portid, u32 seq, int event, u32 flags,
int family, const struct nft_table *table,
const struct nft_chain *chain,
const struct list_head *hook_list)
const struct list_head *hook_list,
const struct list_head *trans_hook_list)
{
struct nlmsghdr *nlh;
@ -2057,7 +2149,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
NFTA_CHAIN_PAD))
goto nla_put_failure;
if (event == NFT_MSG_DELCHAIN && !hook_list) {
if (event == NFT_MSG_DELCHAIN && !hook_list && !trans_hook_list) {
nlmsg_end(skb, nlh);
return 0;
}
@ -2066,7 +2158,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
const struct nft_base_chain *basechain = nft_base_chain(chain);
struct nft_stats __percpu *stats;
if (nft_dump_basechain_hook(skb, net, family, basechain, hook_list))
if (nft_dump_basechain_hook(skb, net, family, basechain,
hook_list, trans_hook_list))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
@ -2102,7 +2195,8 @@ nla_put_failure:
}
static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
const struct list_head *hook_list)
const struct list_head *hook_list,
const struct list_head *trans_hook_list)
{
struct nftables_pernet *nft_net;
struct sk_buff *skb;
@ -2122,7 +2216,7 @@ static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
event, flags, ctx->family, ctx->table,
ctx->chain, hook_list);
ctx->chain, hook_list, trans_hook_list);
if (err < 0) {
kfree_skb(skb);
goto err;
@ -2168,7 +2262,7 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
NFT_MSG_NEWCHAIN,
NLM_F_MULTI,
table->family, table,
chain, NULL) < 0)
chain, NULL, NULL) < 0)
goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@ -2222,7 +2316,7 @@ static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info,
err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN,
0, family, table, chain, NULL);
0, family, table, chain, NULL, NULL);
if (err < 0)
goto err_fill_chain_info;
@ -2315,10 +2409,8 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
list_for_each_entry_safe(hook, next,
&basechain->hook_list, list) {
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
&basechain->hook_list, list)
nft_netdev_hook_unlink_free_rcu(hook);
}
module_put(basechain->type->owner);
if (rcu_access_pointer(basechain->stats)) {
@ -2387,8 +2479,12 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
list_for_each_entry(hook, hook_list, list) {
if (!strncmp(hook->ifname, this->ifname,
min(hook->ifnamelen, this->ifnamelen)))
min(hook->ifnamelen, this->ifnamelen))) {
if (hook->flags & NFT_HOOK_REMOVE)
continue;
return hook;
}
}
return NULL;
@ -3018,6 +3114,7 @@ err_hooks:
list_for_each_entry(ops, &h->ops_list, list)
nf_unregister_net_hook(ctx->net, ops);
}
/* hook.list is on stack, no need for list_del_rcu() */
list_del(&h->list);
nft_netdev_hook_free_rcu(h);
}
@ -3146,6 +3243,32 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
return nf_tables_addchain(&ctx, family, policy, flags, extack);
}
static int nft_trans_delhook(struct nft_hook *hook,
struct list_head *del_list)
{
struct nft_trans_hook *trans_hook;
trans_hook = kmalloc(sizeof(*trans_hook), GFP_KERNEL);
if (!trans_hook)
return -ENOMEM;
trans_hook->hook = hook;
list_add_tail(&trans_hook->list, del_list);
hook->flags |= NFT_HOOK_REMOVE;
return 0;
}
static void nft_trans_delhook_abort(struct list_head *del_list)
{
struct nft_trans_hook *trans_hook, *next;
list_for_each_entry_safe(trans_hook, next, del_list, list) {
trans_hook->hook->flags &= ~NFT_HOOK_REMOVE;
nft_trans_hook_destroy(trans_hook);
}
}
static int nft_delchain_hook(struct nft_ctx *ctx,
struct nft_base_chain *basechain,
struct netlink_ext_ack *extack)
@ -3172,7 +3295,10 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
err = -ENOENT;
goto err_chain_del_hook;
}
list_move(&hook->list, &chain_del_list);
if (nft_trans_delhook(hook, &chain_del_list) < 0) {
err = -ENOMEM;
goto err_chain_del_hook;
}
}
trans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN);
@ -3192,7 +3318,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
return 0;
err_chain_del_hook:
list_splice(&chain_del_list, &basechain->hook_list);
nft_trans_delhook_abort(&chain_del_list);
nft_chain_release_hook(&chain_hook);
return err;
@ -4183,6 +4309,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
struct nft_chain *chain;
struct nft_ctx ctx = {
.net = net,
.table = (struct nft_table *)table,
.family = table->family,
};
int err = 0;
@ -5918,7 +6045,6 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx,
nft_clear(ctx->net, ext);
nft_setelem_data_activate(ctx->net, set, catchall->elem);
break;
}
}
@ -6855,8 +6981,8 @@ static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
}
}
static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_set_elem_expr *elem_expr)
void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_set_elem_expr *elem_expr)
{
struct nft_expr *expr;
u32 size;
@ -7283,6 +7409,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
struct nft_data_desc desc;
enum nft_registers dreg;
struct nft_trans *trans;
bool set_full = false;
u64 expiration;
u64 timeout;
int err, i;
@ -7569,10 +7696,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (err < 0)
goto err_elem_free;
if (!(flags & NFT_SET_ELEM_CATCHALL)) {
unsigned int max = nft_set_maxsize(set), nelems;
nelems = atomic_inc_return(&set->nelems);
if (nelems > max)
set_full = true;
}
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL) {
err = -ENOMEM;
goto err_elem_free;
goto err_set_size;
}
ext->genmask = nft_genmask_cur(ctx->net);
@ -7624,7 +7759,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
ue->priv = elem_priv;
nft_trans_commit_list_add_elem(ctx->net, trans);
goto err_elem_free;
goto err_set_size;
}
}
}
@ -7637,23 +7772,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
goto err_element_clash;
}
if (!(flags & NFT_SET_ELEM_CATCHALL)) {
unsigned int max = nft_set_maxsize(set);
if (!atomic_add_unless(&set->nelems, 1, max)) {
err = -ENFILE;
goto err_set_full;
}
}
nft_trans_container_elem(trans)->elems[0].priv = elem.priv;
nft_trans_commit_list_add_elem(ctx->net, trans);
return 0;
err_set_full:
nft_setelem_remove(ctx->net, set, elem.priv);
return set_full ? -ENFILE : 0;
err_element_clash:
kfree(trans);
err_set_size:
if (!(flags & NFT_SET_ELEM_CATCHALL))
atomic_dec(&set->nelems);
err_elem_free:
nf_tables_set_elem_destroy(ctx, set, elem.priv);
err_parse_data:
@ -9049,10 +9177,8 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net,
list_for_each_entry_safe(hook, next, hook_list, list) {
list_for_each_entry(ops, &hook->ops_list, list)
nft_unregister_flowtable_ops(net, flowtable, ops);
if (release_netdev) {
list_del(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
if (release_netdev)
nft_netdev_hook_unlink_free_rcu(hook);
}
}
@ -9123,8 +9249,7 @@ err_unregister_net_hooks:
nft_unregister_flowtable_ops(net, flowtable, ops);
}
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
nft_netdev_hook_unlink_free_rcu(hook);
}
return err;
@ -9134,9 +9259,25 @@ static void nft_hooks_destroy(struct list_head *hook_list)
{
struct nft_hook *hook, *next;
list_for_each_entry_safe(hook, next, hook_list, list) {
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
list_for_each_entry_safe(hook, next, hook_list, list)
nft_netdev_hook_unlink_free_rcu(hook);
}
static void nft_flowtable_unregister_trans_hook(struct net *net,
struct nft_flowtable *flowtable,
struct list_head *hook_list)
{
struct nft_trans_hook *trans_hook, *next;
struct nf_hook_ops *ops;
struct nft_hook *hook;
list_for_each_entry_safe(trans_hook, next, hook_list, list) {
hook = trans_hook->hook;
list_for_each_entry(ops, &hook->ops_list, list)
nft_unregister_flowtable_ops(net, flowtable, ops);
nft_netdev_hook_unlink_free_rcu(hook);
nft_trans_hook_destroy(trans_hook);
}
}
@ -9225,8 +9366,7 @@ err_flowtable_update_hook:
nft_unregister_flowtable_ops(ctx->net,
flowtable, ops);
}
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
nft_netdev_hook_unlink_free_rcu(hook);
}
return err;
@ -9399,7 +9539,10 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
err = -ENOENT;
goto err_flowtable_del_hook;
}
list_move(&hook->list, &flowtable_del_list);
if (nft_trans_delhook(hook, &flowtable_del_list) < 0) {
err = -ENOMEM;
goto err_flowtable_del_hook;
}
}
trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
@ -9420,7 +9563,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
return 0;
err_flowtable_del_hook:
list_splice(&flowtable_del_list, &flowtable->hook_list);
nft_trans_delhook_abort(&flowtable_del_list);
nft_flowtable_hook_release(&flowtable_hook);
return err;
@ -9485,8 +9628,10 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
u32 portid, u32 seq, int event,
u32 flags, int family,
struct nft_flowtable *flowtable,
struct list_head *hook_list)
struct list_head *hook_list,
struct list_head *trans_hook_list)
{
struct nft_trans_hook *trans_hook;
struct nlattr *nest, *nest_devs;
struct nft_hook *hook;
struct nlmsghdr *nlh;
@ -9503,7 +9648,7 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
NFTA_FLOWTABLE_PAD))
goto nla_put_failure;
if (event == NFT_MSG_DELFLOWTABLE && !hook_list) {
if (event == NFT_MSG_DELFLOWTABLE && !hook_list && !trans_hook_list) {
nlmsg_end(skb, nlh);
return 0;
}
@ -9523,13 +9668,20 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
if (!nest_devs)
goto nla_put_failure;
if (!hook_list)
if (!hook_list && !trans_hook_list)
hook_list = &flowtable->hook_list;
list_for_each_entry_rcu(hook, hook_list, list,
lockdep_commit_lock_is_held(net)) {
if (nft_nla_put_hook_dev(skb, hook))
goto nla_put_failure;
if (hook_list) {
list_for_each_entry_rcu(hook, hook_list, list,
lockdep_commit_lock_is_held(net)) {
if (nft_nla_put_hook_dev(skb, hook))
goto nla_put_failure;
}
} else if (trans_hook_list) {
list_for_each_entry(trans_hook, trans_hook_list, list) {
if (nft_nla_put_hook_dev(skb, trans_hook->hook))
goto nla_put_failure;
}
}
nla_nest_end(skb, nest_devs);
nla_nest_end(skb, nest);
@ -9583,7 +9735,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
NFT_MSG_NEWFLOWTABLE,
NLM_F_MULTI | NLM_F_APPEND,
table->family,
flowtable, NULL) < 0)
flowtable, NULL, NULL) < 0)
goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@ -9683,7 +9835,7 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq,
NFT_MSG_NEWFLOWTABLE, 0, family,
flowtable, NULL);
flowtable, NULL, NULL);
if (err < 0)
goto err_fill_flowtable_info;
@ -9696,7 +9848,9 @@ err_fill_flowtable_info:
static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
struct nft_flowtable *flowtable,
struct list_head *hook_list, int event)
struct list_head *hook_list,
struct list_head *trans_hook_list,
int event)
{
struct nftables_pernet *nft_net = nft_pernet(ctx->net);
struct sk_buff *skb;
@ -9716,7 +9870,8 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
ctx->seq, event, flags,
ctx->family, flowtable, hook_list);
ctx->family, flowtable,
hook_list, trans_hook_list);
if (err < 0) {
kfree_skb(skb);
goto err;
@ -9730,13 +9885,8 @@ err:
static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
{
struct nft_hook *hook, *next;
flowtable->data.type->free(&flowtable->data);
list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
list_del_rcu(&hook->list);
nft_netdev_hook_free_rcu(hook);
}
nft_hooks_destroy(&flowtable->hook_list);
kfree(flowtable->name);
module_put(flowtable->data.type->owner);
kfree(flowtable);
@ -10255,9 +10405,7 @@ static void nft_commit_release(struct nft_trans *trans)
break;
case NFT_MSG_DELCHAIN:
case NFT_MSG_DESTROYCHAIN:
if (nft_trans_chain_update(trans))
nft_hooks_destroy(&nft_trans_chain_hooks(trans));
else
if (!nft_trans_chain_update(trans))
nf_tables_chain_destroy(nft_trans_chain(trans));
break;
case NFT_MSG_DELRULE:
@ -10278,9 +10426,7 @@ static void nft_commit_release(struct nft_trans *trans)
break;
case NFT_MSG_DELFLOWTABLE:
case NFT_MSG_DESTROYFLOWTABLE:
if (nft_trans_flowtable_update(trans))
nft_hooks_destroy(&nft_trans_flowtable_hooks(trans));
else
if (!nft_trans_flowtable_update(trans))
nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
break;
}
@ -10623,11 +10769,6 @@ static void nft_trans_gc_queue_work(struct nft_trans_gc *trans)
schedule_work(&trans_gc_work);
}
static int nft_trans_gc_space(struct nft_trans_gc *trans)
{
return NFT_TRANS_GC_BATCHCOUNT - trans->count;
}
struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
unsigned int gc_seq, gfp_t gfp)
{
@ -11060,31 +11201,28 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
if (nft_trans_chain_update(trans)) {
nft_chain_commit_update(nft_trans_container_chain(trans));
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN,
&nft_trans_chain_hooks(trans));
list_splice(&nft_trans_chain_hooks(trans),
&nft_trans_basechain(trans)->hook_list);
&nft_trans_chain_hooks(trans), NULL);
list_splice_rcu(&nft_trans_chain_hooks(trans),
&nft_trans_basechain(trans)->hook_list);
/* trans destroyed after rcu grace period */
} else {
nft_chain_commit_drop_policy(nft_trans_container_chain(trans));
nft_clear(net, nft_trans_chain(trans));
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL);
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL, NULL);
nft_trans_destroy(trans);
}
break;
case NFT_MSG_DELCHAIN:
case NFT_MSG_DESTROYCHAIN:
if (nft_trans_chain_update(trans)) {
nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, NULL,
&nft_trans_chain_hooks(trans));
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
nft_netdev_unregister_hooks(net,
&nft_trans_chain_hooks(trans),
true);
}
nft_netdev_unregister_trans_hook(net, table,
&nft_trans_chain_hooks(trans));
} else {
nft_chain_del(nft_trans_chain(trans));
nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
NULL);
NULL, NULL);
nf_tables_unregister_hook(ctx.net, ctx.table,
nft_trans_chain(trans));
}
@ -11190,14 +11328,16 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_flowtable_notify(&ctx,
nft_trans_flowtable(trans),
&nft_trans_flowtable_hooks(trans),
NULL,
NFT_MSG_NEWFLOWTABLE);
list_splice(&nft_trans_flowtable_hooks(trans),
&nft_trans_flowtable(trans)->hook_list);
list_splice_rcu(&nft_trans_flowtable_hooks(trans),
&nft_trans_flowtable(trans)->hook_list);
} else {
nft_clear(net, nft_trans_flowtable(trans));
nf_tables_flowtable_notify(&ctx,
nft_trans_flowtable(trans),
NULL,
NULL,
NFT_MSG_NEWFLOWTABLE);
}
nft_trans_destroy(trans);
@ -11207,16 +11347,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
if (nft_trans_flowtable_update(trans)) {
nf_tables_flowtable_notify(&ctx,
nft_trans_flowtable(trans),
NULL,
&nft_trans_flowtable_hooks(trans),
trans->msg_type);
nft_unregister_flowtable_net_hooks(net,
nft_trans_flowtable(trans),
&nft_trans_flowtable_hooks(trans));
nft_flowtable_unregister_trans_hook(net,
nft_trans_flowtable(trans),
&nft_trans_flowtable_hooks(trans));
} else {
list_del_rcu(&nft_trans_flowtable(trans)->list);
nf_tables_flowtable_notify(&ctx,
nft_trans_flowtable(trans),
NULL,
NULL,
trans->msg_type);
nft_unregister_flowtable_net_hooks(net,
nft_trans_flowtable(trans),
@ -11358,11 +11500,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
break;
case NFT_MSG_NEWCHAIN:
if (nft_trans_chain_update(trans)) {
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
nft_netdev_unregister_hooks(net,
&nft_trans_chain_hooks(trans),
true);
}
nft_netdev_unregister_hooks(net, table,
&nft_trans_chain_hooks(trans),
true);
free_percpu(nft_trans_chain_stats(trans));
kfree(nft_trans_chain_name(trans));
nft_trans_destroy(trans);
@ -11380,8 +11520,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
case NFT_MSG_DELCHAIN:
case NFT_MSG_DESTROYCHAIN:
if (nft_trans_chain_update(trans)) {
list_splice(&nft_trans_chain_hooks(trans),
&nft_trans_basechain(trans)->hook_list);
nft_trans_delhook_abort(&nft_trans_chain_hooks(trans));
} else {
nft_use_inc_restore(&table->use);
nft_clear(trans->net, nft_trans_chain(trans));
@ -11495,8 +11634,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
case NFT_MSG_DELFLOWTABLE:
case NFT_MSG_DESTROYFLOWTABLE:
if (nft_trans_flowtable_update(trans)) {
list_splice(&nft_trans_flowtable_hooks(trans),
&nft_trans_flowtable(trans)->hook_list);
nft_trans_delhook_abort(&nft_trans_flowtable_hooks(trans));
} else {
nft_use_inc_restore(&table->use);
nft_clear(trans->net, nft_trans_flowtable(trans));
@ -11809,8 +11947,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
switch (data->verdict.code) {
case NF_ACCEPT:
case NF_DROP:
case NF_QUEUE:
break;
case NFT_CONTINUE:
case NFT_BREAK:
case NFT_RETURN:
@ -11845,6 +11981,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->verdict.chain = chain;
break;
case NF_QUEUE:
/* The nft_queue expression is used for this purpose, an
* immediate NF_QUEUE verdict should not ever be seen here.
*/
fallthrough;
default:
return -EINVAL;
}

View File

@ -361,10 +361,10 @@ static void
__nfulnl_send(struct nfulnl_instance *inst)
{
if (inst->qlen > 1) {
struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0,
NLMSG_DONE,
sizeof(struct nfgenmsg),
0);
struct nlmsghdr *nlh = nfnl_msg_put(inst->skb, 0, 0,
NLMSG_DONE, 0,
AF_UNSPEC, NFNETLINK_V0,
htons(inst->group_num));
if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n",
inst->skb->len, skb_tailroom(inst->skb))) {
kfree_skb(inst->skb);
@ -450,6 +450,23 @@ nla_put_failure:
return -1;
}
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
static int nflog_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
/* This is an inline function, we don't really care about a long
* list of arguments */
static inline int
@ -504,8 +521,7 @@ __build_packet_message(struct nfnl_log_net *log,
/* rcu_read_lock()ed by nf_hook_thresh or
* nf_log_packet.
*/
nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV,
htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_INDEV, indev))
goto nla_put_failure;
} else {
int physinif;
@ -541,8 +557,7 @@ __build_packet_message(struct nfnl_log_net *log,
/* rcu_read_lock()ed by nf_hook_thresh or
* nf_log_packet.
*/
nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV,
htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_OUTDEV, outdev))
goto nla_put_failure;
} else {
struct net_device *physoutdev;
@ -647,15 +662,11 @@ __build_packet_message(struct nfnl_log_net *log,
if (data_len) {
struct nlattr *nla;
int size = nla_attr_size(data_len);
if (skb_tailroom(inst->skb) < nla_total_size(data_len))
nla = nla_reserve(inst->skb, NFULA_PAYLOAD, data_len);
if (!nla)
goto nla_put_failure;
nla = skb_put(inst->skb, nla_total_size(data_len));
nla->nla_type = NFULA_PAYLOAD;
nla->nla_len = size;
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
BUG();
}
@ -730,7 +741,7 @@ nfulnl_log_packet(struct net *net,
+ nla_total_size(plen) /* prefix */
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
+ nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
+ nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
if (in && skb_mac_header_was_set(skb)) {
size += nla_total_size(skb->dev->hard_header_len)

View File

@ -31,26 +31,18 @@ EXPORT_SYMBOL_GPL(nf_osf_fingers);
static inline int nf_osf_ttl(const struct sk_buff *skb,
int ttl_check, unsigned char f_ttl)
{
struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
const struct iphdr *ip = ip_hdr(skb);
const struct in_ifaddr *ifa;
int ret = 0;
if (ttl_check == NF_OSF_TTL_TRUE)
switch (ttl_check) {
case NF_OSF_TTL_TRUE:
return ip->ttl == f_ttl;
if (ttl_check == NF_OSF_TTL_NOCHECK)
break;
case NF_OSF_TTL_NOCHECK:
return 1;
else if (ip->ttl <= f_ttl)
return 1;
in_dev_for_each_ifa_rcu(ifa, in_dev) {
if (inet_ifa_match(ip->saddr, ifa)) {
ret = (ip->ttl == f_ttl);
break;
}
case NF_OSF_TTL_LESS:
default:
return ip->ttl <= f_ttl;
}
return ret;
}
struct nf_osf_hdr_ctx {
@ -64,9 +56,9 @@ struct nf_osf_hdr_ctx {
static bool nf_osf_match_one(const struct sk_buff *skb,
const struct nf_osf_user_finger *f,
int ttl_check,
struct nf_osf_hdr_ctx *ctx)
const struct nf_osf_hdr_ctx *ctx)
{
const __u8 *optpinit = ctx->optp;
const __u8 *optp = ctx->optp;
unsigned int check_WSS = 0;
int fmatch = FMATCH_WRONG;
int foptsize, optnum;
@ -95,17 +87,17 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
check_WSS = f->wss.wc;
for (optnum = 0; optnum < f->opt_num; ++optnum) {
if (f->opt[optnum].kind == *ctx->optp) {
if (f->opt[optnum].kind == *optp) {
__u32 len = f->opt[optnum].length;
const __u8 *optend = ctx->optp + len;
const __u8 *optend = optp + len;
fmatch = FMATCH_OK;
switch (*ctx->optp) {
switch (*optp) {
case OSFOPT_MSS:
mss = ctx->optp[3];
mss = optp[3];
mss <<= 8;
mss |= ctx->optp[2];
mss |= optp[2];
mss = ntohs((__force __be16)mss);
break;
@ -113,7 +105,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
break;
}
ctx->optp = optend;
optp = optend;
} else
fmatch = FMATCH_OPT_WRONG;
@ -156,9 +148,6 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
}
}
if (fmatch != FMATCH_OK)
ctx->optp = optpinit;
return fmatch == FMATCH_OK;
}
@ -302,7 +291,9 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
{
struct nf_osf_user_finger *f;
struct nf_osf_finger *kf = NULL, *sf;
unsigned int tot_opt_len = 0;
int err = 0;
int i;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@ -318,6 +309,21 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
if (f->opt_num > ARRAY_SIZE(f->opt))
return -EINVAL;
if (f->wss.wc >= OSF_WSS_MAX ||
(f->wss.wc == OSF_WSS_MODULO && f->wss.val == 0))
return -EINVAL;
for (i = 0; i < f->opt_num; i++) {
if (!f->opt[i].length || f->opt[i].length > MAX_IPOPTLEN)
return -EINVAL;
if (f->opt[i].kind == OSFOPT_MSS && f->opt[i].length < 4)
return -EINVAL;
tot_opt_len += f->opt[i].length;
if (tot_opt_len > MAX_IPOPTLEN)
return -EINVAL;
}
if (!memchr(f->genre, 0, MAXGENRELEN) ||
!memchr(f->subtype, 0, MAXGENRELEN) ||
!memchr(f->version, 0, MAXGENRELEN))

View File

@ -369,10 +369,47 @@ next_hook:
nf_queue_entry_free(entry);
}
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) {
@ -548,6 +585,23 @@ static int nf_queue_checksum_help(struct sk_buff *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 struct sk_buff *
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
@ -681,10 +735,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
* netfilter_bridge) */
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex)) ||
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
nla_put_be32(skb, NFQA_IFINDEX_INDEV,
htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_INDEV, indev))
goto nla_put_failure;
} else {
int physinif;
@ -715,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
* netfilter_bridge) */
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex)) ||
/* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
nla_put_be32(skb, NFQA_IFINDEX_OUTDEV,
htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_OUTDEV, outdev))
goto nla_put_failure;
} else {
int physoutif;
@ -1061,6 +1109,9 @@ nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int di
{
struct sk_buff *nskb;
if (e->state.net->user_ns != &init_user_ns)
return -EPERM;
if (diff < 0) {
unsigned int min_len = skb_transport_offset(e->skb);
@ -1132,6 +1183,8 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
if (physinif == ifindex || physoutif == 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;
@ -1443,8 +1496,10 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
if (entry->state.pf == PF_BRIDGE) {
err = nfqa_parse_bridge(entry, nfqa);
if (err < 0)
if (err < 0) {
nfqnl_reinject(entry, NF_DROP);
return err;
}
}
if (nfqa[NFQA_PAYLOAD]) {
@ -1454,8 +1509,7 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
payload_len, entry, diff) < 0)
verdict = NF_DROP;
if (ct && diff)
else if (ct && diff)
nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff);
}

View File

@ -260,10 +260,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return ret;
}
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
nft_compat_wait_for_destructors(ctx->net);
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
ret = xt_check_target(&par, size, proto, inv);
if (ret < 0) {
if (ret == -ENOENT) {
@ -352,8 +352,6 @@ nla_put_failure:
static int nft_target_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct xt_target *target = expr->ops->data;
unsigned int hook_mask = 0;
int ret;
if (ctx->family != NFPROTO_IPV4 &&
@ -376,11 +374,21 @@ static int nft_target_validate(const struct nft_ctx *ctx,
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
unsigned int hook_mask = 1 << ops->hooknum;
struct xt_target *target = expr->ops->data;
void *info = nft_expr_priv(expr);
struct xt_tgchk_param par;
union nft_entry e = {};
hook_mask = 1 << ops->hooknum;
if (target->hooks && !(hook_mask & target->hooks))
return -EINVAL;
nft_target_set_tgchk_param(&par, ctx, target, info, &e, 0, false);
ret = xt_check_hooks_target(&par);
if (ret < 0)
return ret;
ret = nft_compat_chain_validate_dependency(ctx, target->table);
if (ret < 0)
return ret;
@ -513,10 +521,10 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return ret;
}
nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
nft_compat_wait_for_destructors(ctx->net);
nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
return xt_check_match(&par, size, proto, inv);
}
@ -612,8 +620,6 @@ static int nft_match_large_dump(struct sk_buff *skb,
static int nft_match_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct xt_match *match = expr->ops->data;
unsigned int hook_mask = 0;
int ret;
if (ctx->family != NFPROTO_IPV4 &&
@ -636,11 +642,30 @@ static int nft_match_validate(const struct nft_ctx *ctx,
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
unsigned int hook_mask = 1 << ops->hooknum;
struct xt_match *match = expr->ops->data;
size_t size = XT_ALIGN(match->matchsize);
struct xt_mtchk_param par;
union nft_entry e = {};
void *info;
hook_mask = 1 << ops->hooknum;
if (match->hooks && !(hook_mask & match->hooks))
return -EINVAL;
if (NFT_EXPR_SIZE(size) > NFT_MATCH_LARGE_THRESH) {
struct nft_xt_match_priv *priv = nft_expr_priv(expr);
info = priv->info;
} else {
info = nft_expr_priv(expr);
}
nft_match_set_mtchk_param(&par, ctx, match, info, &e, 0, false);
ret = xt_check_hooks_match(&par);
if (ret < 0)
return ret;
ret = nft_compat_chain_validate_dependency(ctx, match->table);
if (ret < 0)
return ret;

View File

@ -77,7 +77,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
break;
}
if (ct == NULL)
if (!ct || nf_ct_is_template(ct))
goto err;
switch (priv->key) {
@ -179,12 +179,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
tuple = &ct->tuplehash[priv->dir].tuple;
switch (priv->key) {
case NFT_CT_SRC:
memcpy(dest, tuple->src.u3.all,
nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
memcpy(dest, tuple->src.u3.all, priv->len);
return;
case NFT_CT_DST:
memcpy(dest, tuple->dst.u3.all,
nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
memcpy(dest, tuple->dst.u3.all, priv->len);
return;
case NFT_CT_PROTO_SRC:
nft_reg_store16(dest, (__force u16)tuple->src.u.all);
@ -1018,7 +1016,7 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx,
nf_ct_untimeout(ctx->net, timeout);
nf_ct_netns_put(ctx->net, ctx->family);
kfree(priv->timeout);
kfree_rcu(priv->timeout, rcu);
}
static int nft_ct_timeout_obj_dump(struct sk_buff *skb,
@ -1378,6 +1376,8 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj,
if (nf_ct_expect_related(exp, 0) != 0)
regs->verdict.code = NF_DROP;
nf_ct_expect_put(exp);
}
static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = {

View File

@ -30,7 +30,7 @@ void nft_ct_get_fast_eval(const struct nft_expr *expr,
break;
}
if (!ct) {
if (!ct || nf_ct_is_template(ct)) {
regs->verdict.code = NFT_BREAK;
return;
}

View File

@ -30,18 +30,26 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv,
const struct nft_set_ext *ext)
{
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
struct nft_ctx ctx = {
.net = read_pnet(&priv->set->net),
.family = priv->set->table->family,
};
struct nft_expr *expr;
int i;
for (i = 0; i < priv->num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
if (nft_expr_clone(expr, priv->expr_array[i], GFP_ATOMIC) < 0)
return -1;
goto err_out;
elem_expr->size += priv->expr_array[i]->ops->size;
}
return 0;
err_out:
nft_set_elem_expr_destroy(&ctx, elem_expr);
return -1;
}
struct nft_elem_priv *nft_dynset_new(struct nft_set *set,

View File

@ -530,6 +530,9 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
return err;
}
if ((flags & NFT_EXTHDR_F_PRESENT) && len != 1)
return -EINVAL;
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
priv->offset = offset;
priv->len = len;

View File

@ -107,6 +107,12 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return -EINVAL;
}
if (priv->flags & NFTA_FIB_F_PRESENT) {
if (priv->result != NFT_FIB_RESULT_OIF)
return -EINVAL;
len = sizeof(u8);
}
err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len);
if (err < 0)

View File

@ -21,6 +21,25 @@ struct nft_fwd_netdev {
u8 sreg_dev;
};
#define NF_RECURSION_LIMIT 2
static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
static bool nf_dev_xmit_recursion(void)
{
return unlikely(__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT);
}
static void nf_dev_xmit_recursion_inc(void)
{
__this_cpu_inc(nf_dup_skb_recursion);
}
static void nf_dev_xmit_recursion_dec(void)
{
__this_cpu_dec(nf_dup_skb_recursion);
}
static void nft_fwd_netdev_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@ -100,7 +119,9 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
int oif = regs->data[priv->sreg_dev];
unsigned int verdict = NF_STOLEN;
struct sk_buff *skb = pkt->skb;
int nhoff = skb_network_offset(skb);
struct net_device *dev;
unsigned int hh_len;
int neigh_table;
switch (priv->nfproto) {
@ -111,7 +132,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
verdict = NFT_BREAK;
goto out;
}
if (skb_try_make_writable(skb, sizeof(*iph))) {
if (skb_ensure_writable(skb, nhoff + sizeof(*iph))) {
verdict = NF_DROP;
goto out;
}
@ -127,7 +148,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
verdict = NFT_BREAK;
goto out;
}
if (skb_try_make_writable(skb, sizeof(*ip6h))) {
if (skb_ensure_writable(skb, nhoff + sizeof(*ip6h))) {
verdict = NF_DROP;
goto out;
}
@ -142,12 +163,34 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
}
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
if (dev == NULL)
return;
if (!dev) {
verdict = NF_DROP;
goto out;
}
local_bh_disable();
if (nf_dev_xmit_recursion()) {
local_bh_enable();
verdict = NF_DROP;
goto out;
}
hh_len = LL_RESERVED_SPACE(dev);
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
skb = skb_expand_head(skb, hh_len);
if (!skb) {
local_bh_enable();
goto out;
}
}
skb->dev = dev;
skb_clear_tstamp(skb);
nf_dev_xmit_recursion_inc();
neigh_xmit(neigh_table, dev, addr, skb);
nf_dev_xmit_recursion_dec();
local_bh_enable();
out:
regs->verdict.code = verdict;
}

View File

@ -28,6 +28,11 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
struct nf_osf_data data;
struct tcphdr _tcph;
if (nft_pf(pkt) != NFPROTO_IPV4) {
regs->verdict.code = NFT_BREAK;
return;
}
if (pkt->tprot != IPPROTO_TCP) {
regs->verdict.code = NFT_BREAK;
return;
@ -114,7 +119,6 @@ static int nft_osf_validate(const struct nft_ctx *ctx,
switch (ctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_INET:
hooks = (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_PRE_ROUTING) |

View File

@ -944,6 +944,9 @@ static int nft_payload_set_init(const struct nft_ctx *ctx,
u32 csum_offset, csum_type = NFT_PAYLOAD_CSUM_NONE;
int err;
if (ctx->net->user_ns != &init_user_ns)
return -EPERM;
priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));

View File

@ -619,15 +619,20 @@ static struct nft_elem_priv *
nft_hash_get(const struct net *net, const struct nft_set *set,
const struct nft_set_elem *elem, unsigned int flags)
{
const u32 *key = (const u32 *)&elem->key.val;
struct nft_hash *priv = nft_set_priv(set);
u8 genmask = nft_genmask_cur(net);
struct nft_hash_elem *he;
u32 hash;
hash = jhash(elem->key.val.data, set->klen, priv->seed);
if (set->klen == 4)
hash = jhash_1word(*key, priv->seed);
else
hash = jhash(key, set->klen, priv->seed);
hash = reciprocal_scale(hash, priv->buckets);
hlist_for_each_entry_rcu(he, &priv->table[hash], node) {
if (!memcmp(nft_set_ext_key(&he->ext), elem->key.val.data, set->klen) &&
if (!memcmp(nft_set_ext_key(&he->ext), key, set->klen) &&
nft_set_elem_active(&he->ext, genmask))
return &he->priv;
}

View File

@ -1626,6 +1626,7 @@ static void pipapo_drop(struct nft_pipapo_match *m,
int i;
nft_pipapo_for_each_field(f, i, m) {
bool last = i == m->field_count - 1;
int g;
for (g = 0; g < f->groups; g++) {
@ -1645,7 +1646,7 @@ static void pipapo_drop(struct nft_pipapo_match *m,
}
pipapo_unmap(f->mt, f->rules, rulemap[i].to, rulemap[i].n,
rulemap[i + 1].n, i == m->field_count - 1);
last ? 0 : rulemap[i + 1].n, last);
if (pipapo_resize(f, f->rules, f->rules - rulemap[i].n)) {
/* We can ignore this, a failure to shrink tables down
* doesn't make tables invalid.
@ -1666,11 +1667,11 @@ static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set,
}
/**
* pipapo_gc() - Drop expired entries from set, destroy start and end elements
* pipapo_gc_scan() - Drop expired entries from set and link them to gc list
* @set: nftables API set representation
* @m: Matching data
*/
static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
static void pipapo_gc_scan(struct nft_set *set, struct nft_pipapo_match *m)
{
struct nft_pipapo *priv = nft_set_priv(set);
struct net *net = read_pnet(&set->net);
@ -1683,6 +1684,8 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
if (!gc)
return;
list_add(&gc->list, &priv->gc_head);
while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
const struct nft_pipapo_field *f;
@ -1710,9 +1713,13 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
* NFT_SET_ELEM_DEAD_BIT.
*/
if (__nft_set_elem_expired(&e->ext, tstamp)) {
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
if (!gc)
return;
if (!nft_trans_gc_space(gc)) {
gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
if (!gc)
return;
list_add(&gc->list, &priv->gc_head);
}
nft_pipapo_gc_deactivate(net, set, e);
pipapo_drop(m, rulemap);
@ -1726,10 +1733,30 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
}
}
gc = nft_trans_gc_catchall_sync(gc);
priv->last_gc = jiffies;
}
/**
* pipapo_gc_queue() - Free expired elements
* @set: nftables API set representation
*/
static void pipapo_gc_queue(struct nft_set *set)
{
struct nft_pipapo *priv = nft_set_priv(set);
struct nft_trans_gc *gc, *next;
/* always do a catchall cycle: */
gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
if (gc) {
gc = nft_trans_gc_catchall_sync(gc);
if (gc)
nft_trans_gc_queue_sync_done(gc);
}
/* always purge queued gc elements. */
list_for_each_entry_safe(gc, next, &priv->gc_head, list) {
list_del(&gc->list);
nft_trans_gc_queue_sync_done(gc);
priv->last_gc = jiffies;
}
}
@ -1783,6 +1810,10 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
*
* We also need to create a new working copy for subsequent insertions and
* deletions.
*
* After the live copy has been replaced by the clone, we can safely queue
* expired elements that have been collected by pipapo_gc_scan() for
* memory reclaim.
*/
static void nft_pipapo_commit(struct nft_set *set)
{
@ -1793,7 +1824,7 @@ static void nft_pipapo_commit(struct nft_set *set)
return;
if (time_after_eq(jiffies, priv->last_gc + nft_set_gc_interval(set)))
pipapo_gc(set, priv->clone);
pipapo_gc_scan(set, priv->clone);
old = rcu_replace_pointer(priv->match, priv->clone,
nft_pipapo_transaction_mutex_held(set));
@ -1801,6 +1832,8 @@ static void nft_pipapo_commit(struct nft_set *set)
if (old)
call_rcu(&old->rcu, pipapo_reclaim_match);
pipapo_gc_queue(set);
}
static void nft_pipapo_abort(const struct nft_set *set)
@ -2258,6 +2291,7 @@ static int nft_pipapo_init(const struct nft_set *set,
f->mt = NULL;
}
INIT_LIST_HEAD(&priv->gc_head);
rcu_assign_pointer(priv->match, m);
return 0;
@ -2307,6 +2341,8 @@ static void nft_pipapo_destroy(const struct nft_ctx *ctx,
struct nft_pipapo *priv = nft_set_priv(set);
struct nft_pipapo_match *m;
WARN_ON_ONCE(!list_empty(&priv->gc_head));
m = rcu_dereference_protected(priv->match, true);
if (priv->clone) {

View File

@ -156,12 +156,14 @@ struct nft_pipapo_match {
* @clone: Copy where pending insertions and deletions are kept
* @width: Total bytes to be matched for one packet, including padding
* @last_gc: Timestamp of last garbage collection run, jiffies
* @gc_head: list of nft_trans_gc to queue up for mem reclaim
*/
struct nft_pipapo {
struct nft_pipapo_match __rcu *match;
struct nft_pipapo_match *clone;
int width;
unsigned long last_gc;
struct list_head gc_head;
};
struct nft_pipapo_elem;

View File

@ -242,7 +242,7 @@ static int nft_pipapo_avx2_lookup_4b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -319,7 +319,7 @@ static int nft_pipapo_avx2_lookup_4b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -414,7 +414,7 @@ static int nft_pipapo_avx2_lookup_4b_8(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -505,7 +505,7 @@ static int nft_pipapo_avx2_lookup_4b_12(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -641,7 +641,7 @@ static int nft_pipapo_avx2_lookup_4b_32(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -699,7 +699,7 @@ static int nft_pipapo_avx2_lookup_8b_1(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -764,7 +764,7 @@ static int nft_pipapo_avx2_lookup_8b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -839,7 +839,7 @@ static int nft_pipapo_avx2_lookup_8b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -925,7 +925,7 @@ static int nft_pipapo_avx2_lookup_8b_6(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@ -1019,7 +1019,7 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
return b;
ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;

View File

@ -705,7 +705,7 @@ static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx,
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
metadata_dst_free(priv->md);
dst_release(&priv->md->dst);
}
static struct nft_object_type nft_tunnel_obj_type;

View File

@ -477,11 +477,9 @@ int xt_check_proc_name(const char *name, unsigned int size)
}
EXPORT_SYMBOL(xt_check_proc_name);
int xt_check_match(struct xt_mtchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
static int xt_check_match_common(struct xt_mtchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
{
int ret;
if (XT_ALIGN(par->match->matchsize) != size &&
par->match->matchsize != -1) {
/*
@ -501,6 +499,17 @@ int xt_check_match(struct xt_mtchk_param *par,
par->match->table, par->table);
return -EINVAL;
}
/* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
* NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
* support.
*/
if (par->family == NFPROTO_ARP &&
par->match->family != NFPROTO_ARP) {
pr_info_ratelimited("%s_tables: %s match: not valid for this family\n",
xt_prefix[par->family], par->match->name);
return -EINVAL;
}
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
char used[64], allow[64];
@ -519,6 +528,14 @@ int xt_check_match(struct xt_mtchk_param *par,
par->match->proto);
return -EINVAL;
}
return 0;
}
static int xt_checkentry_match(struct xt_mtchk_param *par)
{
int ret;
if (par->match->checkentry != NULL) {
ret = par->match->checkentry(par);
if (ret < 0)
@ -527,8 +544,34 @@ int xt_check_match(struct xt_mtchk_param *par,
/* Flag up potential errors. */
return -EIO;
}
return 0;
}
int xt_check_hooks_match(struct xt_mtchk_param *par)
{
if (par->match->check_hooks != NULL)
return par->match->check_hooks(par);
return 0;
}
EXPORT_SYMBOL_GPL(xt_check_hooks_match);
int xt_check_match(struct xt_mtchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
{
int ret;
ret = xt_check_match_common(par, size, proto, inv_proto);
if (ret < 0)
return ret;
ret = xt_check_hooks_match(par);
if (ret < 0)
return ret;
return xt_checkentry_match(par);
}
EXPORT_SYMBOL_GPL(xt_check_match);
/** xt_check_entry_match - check that matches end before start of target
@ -997,11 +1040,9 @@ bool xt_find_jump_offset(const unsigned int *offsets,
}
EXPORT_SYMBOL(xt_find_jump_offset);
int xt_check_target(struct xt_tgchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
static int xt_check_target_common(struct xt_tgchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
{
int ret;
if (XT_ALIGN(par->target->targetsize) != size) {
pr_err_ratelimited("%s_tables: %s.%u target: invalid size %u (kernel) != (user) %u\n",
xt_prefix[par->family], par->target->name,
@ -1016,6 +1057,18 @@ int xt_check_target(struct xt_tgchk_param *par,
par->target->table, par->table);
return -EINVAL;
}
/* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
* NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
* support.
*/
if (par->family == NFPROTO_ARP &&
par->target->family != NFPROTO_ARP) {
pr_info_ratelimited("%s_tables: %s target: not valid for this family\n",
xt_prefix[par->family], par->target->name);
return -EINVAL;
}
if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
char used[64], allow[64];
@ -1034,6 +1087,23 @@ int xt_check_target(struct xt_tgchk_param *par,
par->target->proto);
return -EINVAL;
}
return 0;
}
int xt_check_hooks_target(struct xt_tgchk_param *par)
{
if (par->target->check_hooks != NULL)
return par->target->check_hooks(par);
return 0;
}
EXPORT_SYMBOL_GPL(xt_check_hooks_target);
static int xt_checkentry_target(struct xt_tgchk_param *par)
{
int ret;
if (par->target->checkentry != NULL) {
ret = par->target->checkentry(par);
if (ret < 0)
@ -1044,6 +1114,22 @@ int xt_check_target(struct xt_tgchk_param *par,
}
return 0;
}
int xt_check_target(struct xt_tgchk_param *par,
unsigned int size, u16 proto, bool inv_proto)
{
int ret;
ret = xt_check_target_common(par, size, proto, inv_proto);
if (ret < 0)
return ret;
ret = xt_check_hooks_target(par);
if (ret < 0)
return ret;
return xt_checkentry_target(par);
}
EXPORT_SYMBOL_GPL(xt_check_target);
/**

View File

@ -350,7 +350,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
.family = NFPROTO_IPV4,
.revision = 1,
.targetsize = sizeof(struct xt_ct_target_info_v1),
.usersize = offsetof(struct xt_ct_target_info, ct),
.usersize = offsetof(struct xt_ct_target_info_v1, ct),
.checkentry = xt_ct_tg_check_v1,
.destroy = xt_ct_tg_destroy_v1,
.target = xt_ct_target_v1,
@ -362,7 +362,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
.family = NFPROTO_IPV4,
.revision = 2,
.targetsize = sizeof(struct xt_ct_target_info_v1),
.usersize = offsetof(struct xt_ct_target_info, ct),
.usersize = offsetof(struct xt_ct_target_info_v1, ct),
.checkentry = xt_ct_tg_check_v2,
.destroy = xt_ct_tg_destroy_v1,
.target = xt_ct_target_v1,
@ -394,7 +394,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
.family = NFPROTO_IPV6,
.revision = 1,
.targetsize = sizeof(struct xt_ct_target_info_v1),
.usersize = offsetof(struct xt_ct_target_info, ct),
.usersize = offsetof(struct xt_ct_target_info_v1, ct),
.checkentry = xt_ct_tg_check_v1,
.destroy = xt_ct_tg_destroy_v1,
.target = xt_ct_target_v1,
@ -406,7 +406,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
.family = NFPROTO_IPV6,
.revision = 2,
.targetsize = sizeof(struct xt_ct_target_info_v1),
.usersize = offsetof(struct xt_ct_target_info, ct),
.usersize = offsetof(struct xt_ct_target_info_v1, ct),
.checkentry = xt_ct_tg_check_v2,
.destroy = xt_ct_tg_destroy_v1,
.target = xt_ct_target_v1,

View File

@ -153,14 +153,10 @@ addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
return ret;
}
static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
static int addrtype_mt_check_hooks(const struct xt_mtchk_param *par)
{
const char *errmsg = "both incoming and outgoing interface limitation cannot be selected";
struct xt_addrtype_info_v1 *info = par->matchinfo;
if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN &&
info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
goto err;
const char *errmsg;
if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN)) &&
@ -176,6 +172,21 @@ static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
goto err;
}
return 0;
err:
pr_info_ratelimited("%s\n", errmsg);
return -EINVAL;
}
static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
{
const char *errmsg = "both incoming and outgoing interface limitation cannot be selected";
struct xt_addrtype_info_v1 *info = par->matchinfo;
if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN &&
info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
goto err;
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
if (par->family == NFPROTO_IPV6) {
if ((info->source | info->dest) & XT_ADDRTYPE_BLACKHOLE) {
@ -211,6 +222,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV4,
.revision = 1,
.match = addrtype_mt_v1,
.check_hooks = addrtype_mt_check_hooks,
.checkentry = addrtype_mt_checkentry_v1,
.matchsize = sizeof(struct xt_addrtype_info_v1),
.me = THIS_MODULE
@ -221,6 +233,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
.family = NFPROTO_IPV6,
.revision = 1,
.match = addrtype_mt_v1,
.check_hooks = addrtype_mt_check_hooks,
.checkentry = addrtype_mt_checkentry_v1,
.matchsize = sizeof(struct xt_addrtype_info_v1),
.me = THIS_MODULE

View File

@ -53,6 +53,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
info->priv = NULL;
if (info->has_path) {
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
return -ENAMETOOLONG;
cgrp = cgroup_get_from_path(info->path);
if (IS_ERR(cgrp)) {
pr_info_ratelimited("invalid path, errno=%ld\n",
@ -85,6 +88,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
info->priv = NULL;
if (info->has_path) {
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
return -ENAMETOOLONG;
cgrp = cgroup_get_from_path(info->path);
if (IS_ERR(cgrp)) {
pr_info_ratelimited("invalid path, errno=%ld\n",

View File

@ -33,14 +33,10 @@ static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
return true;
}
static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
static int devgroup_mt_check_hooks(const struct xt_mtchk_param *par)
{
const struct xt_devgroup_info *info = par->matchinfo;
if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC |
XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST))
return -EINVAL;
if (info->flags & XT_DEVGROUP_MATCH_SRC &&
par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN) |
@ -56,9 +52,21 @@ static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
return 0;
}
static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
{
const struct xt_devgroup_info *info = par->matchinfo;
if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC |
XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST))
return -EINVAL;
return 0;
}
static struct xt_match devgroup_mt_reg __read_mostly = {
.name = "devgroup",
.match = devgroup_mt,
.check_hooks = devgroup_mt_check_hooks,
.checkentry = devgroup_mt_checkentry,
.matchsize = sizeof(struct xt_devgroup_info),
.family = NFPROTO_UNSPEC,

View File

@ -38,25 +38,37 @@ static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par)
return ret;
}
static struct xt_match mac_mt_reg __read_mostly = {
.name = "mac",
.revision = 0,
.family = NFPROTO_UNSPEC,
.match = mac_mt,
.matchsize = sizeof(struct xt_mac_info),
.hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD),
.me = THIS_MODULE,
static struct xt_match mac_mt_reg[] __read_mostly = {
{
.name = "mac",
.family = NFPROTO_IPV4,
.match = mac_mt,
.matchsize = sizeof(struct xt_mac_info),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD),
.me = THIS_MODULE,
},
{
.name = "mac",
.family = NFPROTO_IPV6,
.match = mac_mt,
.matchsize = sizeof(struct xt_mac_info),
.hooks = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD),
.me = THIS_MODULE,
},
};
static int __init mac_mt_init(void)
{
return xt_register_match(&mac_mt_reg);
return xt_register_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg));
}
static void __exit mac_mt_exit(void)
{
xt_unregister_match(&mac_mt_reg);
xt_unregister_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg));
}
module_init(mac_mt_init);

View File

@ -105,6 +105,28 @@ multiport_mt(const struct sk_buff *skb, struct xt_action_param *par)
return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
}
static bool
multiport_valid_ranges(const struct xt_multiport_v1 *multiinfo)
{
unsigned int i;
for (i = 0; i < multiinfo->count; i++) {
if (!multiinfo->pflags[i])
continue;
if (++i >= multiinfo->count)
return false;
if (multiinfo->pflags[i])
return false;
if (multiinfo->ports[i - 1] > multiinfo->ports[i])
return false;
}
return true;
}
static inline bool
check(u_int16_t proto,
u_int8_t ip_invflags,
@ -127,8 +149,10 @@ static int multiport_mt_check(const struct xt_mtchk_param *par)
const struct ipt_ip *ip = par->entryinfo;
const struct xt_multiport_v1 *multiinfo = par->matchinfo;
return check(ip->proto, ip->invflags, multiinfo->flags,
multiinfo->count) ? 0 : -EINVAL;
if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
return -EINVAL;
return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
}
static int multiport_mt6_check(const struct xt_mtchk_param *par)
@ -136,8 +160,10 @@ static int multiport_mt6_check(const struct xt_mtchk_param *par)
const struct ip6t_ip6 *ip = par->entryinfo;
const struct xt_multiport_v1 *multiinfo = par->matchinfo;
return check(ip->proto, ip->invflags, multiinfo->flags,
multiinfo->count) ? 0 : -EINVAL;
if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
return -EINVAL;
return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
}
static struct xt_match multiport_mt_reg[] __read_mostly = {

View File

@ -127,26 +127,39 @@ owner_mt(const struct sk_buff *skb, struct xt_action_param *par)
return true;
}
static struct xt_match owner_mt_reg __read_mostly = {
.name = "owner",
.revision = 1,
.family = NFPROTO_UNSPEC,
.checkentry = owner_check,
.match = owner_mt,
.matchsize = sizeof(struct xt_owner_match_info),
.hooks = (1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_POST_ROUTING),
.me = THIS_MODULE,
static struct xt_match owner_mt_reg[] __read_mostly = {
{
.name = "owner",
.revision = 1,
.family = NFPROTO_IPV4,
.checkentry = owner_check,
.match = owner_mt,
.matchsize = sizeof(struct xt_owner_match_info),
.hooks = (1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_POST_ROUTING),
.me = THIS_MODULE,
},
{
.name = "owner",
.revision = 1,
.family = NFPROTO_IPV6,
.checkentry = owner_check,
.match = owner_mt,
.matchsize = sizeof(struct xt_owner_match_info),
.hooks = (1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_POST_ROUTING),
.me = THIS_MODULE,
}
};
static int __init owner_mt_init(void)
{
return xt_register_match(&owner_mt_reg);
return xt_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
}
static void __exit owner_mt_exit(void)
{
xt_unregister_match(&owner_mt_reg);
xt_unregister_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
}
module_init(owner_mt_init);

View File

@ -91,6 +91,21 @@ match_outdev:
return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
}
static int physdev_mt_check_hooks(const struct xt_mtchk_param *par)
{
const struct xt_physdev_info *info = par->matchinfo;
if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
(!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
info->invert & XT_PHYSDEV_OP_BRIDGED) &&
par->hook_mask & (1 << NF_INET_LOCAL_OUT)) {
pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n");
return -EINVAL;
}
return 0;
}
static int physdev_mt_check(const struct xt_mtchk_param *par)
{
const struct xt_physdev_info *info = par->matchinfo;
@ -99,13 +114,6 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
info->bitmask & ~XT_PHYSDEV_OP_MASK)
return -EINVAL;
if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
(!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
info->invert & XT_PHYSDEV_OP_BRIDGED) &&
par->hook_mask & (1 << NF_INET_LOCAL_OUT)) {
pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n");
return -EINVAL;
}
if (!brnf_probed) {
brnf_probed = true;
@ -115,24 +123,35 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
return 0;
}
static struct xt_match physdev_mt_reg __read_mostly = {
.name = "physdev",
.revision = 0,
.family = NFPROTO_UNSPEC,
.checkentry = physdev_mt_check,
.match = physdev_mt,
.matchsize = sizeof(struct xt_physdev_info),
.me = THIS_MODULE,
static struct xt_match physdev_mt_reg[] __read_mostly = {
{
.name = "physdev",
.family = NFPROTO_IPV4,
.check_hooks = physdev_mt_check_hooks,
.checkentry = physdev_mt_check,
.match = physdev_mt,
.matchsize = sizeof(struct xt_physdev_info),
.me = THIS_MODULE,
},
{
.name = "physdev",
.family = NFPROTO_IPV6,
.check_hooks = physdev_mt_check_hooks,
.checkentry = physdev_mt_check,
.match = physdev_mt,
.matchsize = sizeof(struct xt_physdev_info),
.me = THIS_MODULE,
},
};
static int __init physdev_mt_init(void)
{
return xt_register_match(&physdev_mt_reg);
return xt_register_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg));
}
static void __exit physdev_mt_exit(void)
{
xt_unregister_match(&physdev_mt_reg);
xt_unregister_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg));
}
module_init(physdev_mt_init);

View File

@ -63,7 +63,7 @@ match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info,
return 0;
for (i = sp->len - 1; i >= 0; i--) {
pos = strict ? i - sp->len + 1 : 0;
pos = strict ? sp->len - i - 1 : 0;
if (pos >= info->len)
return 0;
e = &info->pol[pos];
@ -126,13 +126,10 @@ policy_mt(const struct sk_buff *skb, struct xt_action_param *par)
return ret;
}
static int policy_mt_check(const struct xt_mtchk_param *par)
static int policy_mt_check_hooks(const struct xt_mtchk_param *par)
{
const struct xt_policy_info *info = par->matchinfo;
const char *errmsg = "neither incoming nor outgoing policy selected";
if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT)))
goto err;
const char *errmsg;
if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT) {
@ -144,6 +141,21 @@ static int policy_mt_check(const struct xt_mtchk_param *par)
errmsg = "input policy not valid in POSTROUTING and OUTPUT";
goto err;
}
return 0;
err:
pr_info_ratelimited("%s\n", errmsg);
return -EINVAL;
}
static int policy_mt_check(const struct xt_mtchk_param *par)
{
const struct xt_policy_info *info = par->matchinfo;
const char *errmsg = "neither incoming nor outgoing policy selected";
if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT)))
goto err;
if (info->len > XT_POLICY_MAX_ELEM) {
errmsg = "too many policy elements";
goto err;
@ -158,6 +170,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
{
.name = "policy",
.family = NFPROTO_IPV4,
.check_hooks = policy_mt_check_hooks,
.checkentry = policy_mt_check,
.match = policy_mt,
.matchsize = sizeof(struct xt_policy_info),
@ -166,6 +179,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
{
.name = "policy",
.family = NFPROTO_IPV6,
.check_hooks = policy_mt_check_hooks,
.checkentry = policy_mt_check,
.match = policy_mt,
.matchsize = sizeof(struct xt_policy_info),

View File

@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par)
goto err1;
}
if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1))
return -ENAMETOOLONG;
if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2))
return -ENAMETOOLONG;
ret = -ENOENT;
est1 = xt_rateest_lookup(par->net, info->name1);
if (!est1)

View File

@ -33,7 +33,7 @@ static struct xt_match realm_mt_reg __read_mostly = {
.matchsize = sizeof(struct xt_realm_info),
.hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_FORWARD) |
(1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN),
.family = NFPROTO_UNSPEC,
.family = NFPROTO_IPV4,
.me = THIS_MODULE
};

View File

@ -430,6 +430,29 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
return XT_CONTINUE;
}
static int
set_target_v3_check_hooks(const struct xt_tgchk_param *par)
{
const struct xt_set_info_target_v3 *info = par->targinfo;
if (info->map_set.index != IPSET_INVALID_ID) {
if (strncmp(par->table, "mangle", 7)) {
pr_info_ratelimited("--map-set only usable from mangle table\n");
return -EINVAL;
}
if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
(info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
(par->hook_mask & ~(1 << NF_INET_FORWARD |
1 << NF_INET_LOCAL_OUT |
1 << NF_INET_POST_ROUTING))) {
pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
return -EINVAL;
}
}
return 0;
}
static int
set_target_v3_checkentry(const struct xt_tgchk_param *par)
{
@ -459,20 +482,6 @@ set_target_v3_checkentry(const struct xt_tgchk_param *par)
}
if (info->map_set.index != IPSET_INVALID_ID) {
if (strncmp(par->table, "mangle", 7)) {
pr_info_ratelimited("--map-set only usable from mangle table\n");
ret = -EINVAL;
goto cleanup_del;
}
if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
(info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
(par->hook_mask & ~(1 << NF_INET_FORWARD |
1 << NF_INET_LOCAL_OUT |
1 << NF_INET_POST_ROUTING))) {
pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
ret = -EINVAL;
goto cleanup_del;
}
index = ip_set_nfnl_get_byindex(par->net,
info->map_set.index);
if (index == IPSET_INVALID_ID) {
@ -672,6 +681,7 @@ static struct xt_target set_targets[] __read_mostly = {
.family = NFPROTO_IPV4,
.target = set_target_v3,
.targetsize = sizeof(struct xt_set_info_target_v3),
.check_hooks = set_target_v3_check_hooks,
.checkentry = set_target_v3_checkentry,
.destroy = set_target_v3_destroy,
.me = THIS_MODULE
@ -682,6 +692,7 @@ static struct xt_target set_targets[] __read_mostly = {
.family = NFPROTO_IPV6,
.target = set_target_v3,
.targetsize = sizeof(struct xt_set_info_target_v3),
.check_hooks = set_target_v3_check_hooks,
.checkentry = set_target_v3_checkentry,
.destroy = set_target_v3_destroy,
.me = THIS_MODULE

View File

@ -112,11 +112,6 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
}
EXPORT_SYMBOL(tcf_action_set_ctrlact);
/* XXX: For standalone actions, we don't need a RCU grace period either, because
* actions are always connected to filters and filters are already destroyed in
* RCU callbacks, so after a RCU grace period actions are already disconnected
* from filters. Readers later can not find us.
*/
static void free_tcf(struct tc_action *p)
{
struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1);
@ -129,7 +124,7 @@ static void free_tcf(struct tc_action *p)
if (chain)
tcf_chain_put_by_act(chain);
kfree(p);
kfree_rcu_mightsleep(p);
}
static void offload_action_hw_count_set(struct tc_action *act,

View File

@ -552,6 +552,8 @@ static void __unix_gc(struct work_struct *work)
struct sk_buff_head hitlist;
struct sk_buff *skb;
WRITE_ONCE(gc_in_progress, true);
spin_lock(&unix_gc_lock);
if (!unix_graph_maybe_cyclic) {

View File

@ -29,7 +29,8 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
net6_port_net6_port net_port_mac_proto_net"
# Reported bugs, also described by TYPE_ variables below
BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch"
BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate
insert_overlap load_flush_load4 load_flush_load8"
# List of possible paths to pktgen script from kernel tree for performance tests
PKTGEN_SCRIPT_PATHS="
@ -408,6 +409,54 @@ perf_duration 0
"
TYPE_doublecreate="
display cannot create same element twice
type_spec ipv4_addr . ipv4_addr
chain_spec ip saddr . ip daddr
dst addr4
proto icmp
race_repeat 0
perf_duration 0
"
TYPE_insert_overlap="
display reject overlapping range on add
type_spec ipv4_addr . ipv4_addr
chain_spec ip saddr . ip daddr
dst addr4
proto icmp
race_repeat 0
perf_duration 0
"
TYPE_load_flush_load4="
display reload with flush, 4bit groups
type_spec ipv4_addr . ipv4_addr
chain_spec ip saddr . ip daddr
dst addr4
proto icmp
race_repeat 0
perf_duration 0
"
TYPE_load_flush_load8="
display reload with flush, 8bit groups
type_spec ipv4_addr . ipv4_addr
chain_spec ip saddr . ip daddr
dst addr4
proto icmp
race_repeat 0
perf_duration 0
"
# Set template for all tests, types and rules are filled in depending on test
set_template='
flush ruleset
@ -1900,6 +1949,128 @@ test_bug_avx2_mismatch()
fi
}
test_bug_doublecreate()
{
local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4"
local ret=1
local i
setup veth send_"${proto}" set || return ${ksft_skip}
add "{ $elements }" || return 1
# expected to work: 'add' on existing should be no-op.
add "{ $elements }" || return 1
# 'create' should return an error.
if nft create element inet filter test "{ $elements }" 2>/dev/null; then
err "Could create an existing element"
return 1
fi
nft -f - <<EOF 2>/dev/null
flush set inet filter test
create element inet filter test { $elements }
create element inet filter test { $elements }
EOF
ret=$?
if [ $ret -eq 0 ]; then
err "Could create element twice in one transaction"
err "$(nft -a list ruleset)"
return 1
fi
nft -f - <<EOF 2>/dev/null
flush set inet filter test
create element inet filter test { $elements }
EOF
ret=$?
if [ $ret -ne 0 ]; then
err "Could not flush and re-create element in one transaction"
return 1
fi
return 0
}
add_fail()
{
if nft add element inet filter test "$1" 2>/dev/null ; then
err "Returned success for add ${1} given set:"
err "$(nft -a list set inet filter test )"
return 1
fi
return 0
}
test_bug_insert_overlap()
{
local elements="1.2.3.4 . 1.2.4.1"
# This test has to be skipped, RHEL-10.2 ntentionally lacks
# 7711f4bb4b36 ("netfilter: nft_set_pipapo: fix range overlap detection")
# because this fix could cause issues with existing deployments
# (ruleset restore failure).
return ${ksft_skip}
setup veth send_"${proto}" set || return ${ksft_skip}
add "{ $elements }" || return 1
elements="1.2.3.0-1.2.3.4 . 1.2.4.1"
add_fail "{ $elements }" || return 1
elements="1.2.3.0-1.2.3.4 . 1.2.4.2"
add "{ $elements }" || return 1
elements="1.2.3.4 . 1.2.4.1-1.2.4.2"
add_fail "{ $elements }" || return 1
return 0
}
test_bug_load_flush_load4()
{
local i
setup veth send_"${proto}" set || return ${ksft_skip}
for i in $(seq 0 255); do
local addelem="add element inet filter test"
local j
for j in $(seq 0 20); do
echo "$addelem { 10.$j.0.$i . 10.$j.1.$i }"
echo "$addelem { 10.$j.0.$i . 10.$j.2.$i }"
done
done > "$tmp"
nft -f "$tmp" || return 1
( echo "flush set inet filter test";cat "$tmp") | nft -f -
[ $? -eq 0 ] || return 1
return 0
}
test_bug_load_flush_load8()
{
local i
setup veth send_"${proto}" set || return ${ksft_skip}
for i in $(seq 1 100); do
echo "add element inet filter test { 10.0.0.$i . 10.0.1.$i }"
echo "add element inet filter test { 10.0.0.$i . 10.0.2.$i }"
done > "$tmp"
nft -f "$tmp" || return 1
( echo "flush set inet filter test";cat "$tmp") | nft -f -
[ $? -eq 0 ] || return 1
return 0
}
test_reported_issues() {
eval test_bug_"${subtest}"
}

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.46.1.el10.x86_64,mailto:security@almalinux.org
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.47.1.el10.x86_64,mailto:security@almalinux.org

View File

@ -1,2 +1,2 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.46.1.el10.x86_64,mailto:security@almalinux.org
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.47.1.el10.x86_64,mailto:security@almalinux.org