Import of kernel-6.12.0-211.47.1.el10_2
This commit is contained in:
parent
55d3eb33dc
commit
1d2c3ecdad
@ -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
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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, ¶m_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)
|
||||
|
||||
@ -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 *,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 *
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
struct nf_ct_timeout {
|
||||
__u16 l3num;
|
||||
const struct nf_conntrack_l4proto *l4proto;
|
||||
struct rcu_head rcu;
|
||||
char data[];
|
||||
};
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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) ||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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 ");
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 },
|
||||
};
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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' &&
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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))) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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] = {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) |
|
||||
|
||||
@ -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]));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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}"
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
2
uki.sbat
2
uki.sbat
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user