Merge tag 'net-7.2-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net

Pull networking fixes from Paolo Abeni:
 "Including fixes from netfilter.

  There is a known WiFi/mt76 regression, waiting for a complete fix that
  should land soonish.

  Previous releases - regressions:

   - tcp: fix icsk_ack.ato bitfield overflow

   - af_unix: Unlink scc_entry in unix_del_edge()

   - ipv4: fix use-after-free in fib_nhc_update_mtu()

   - netfilter:
      - ipset: fix refcount race between list:set GC and swap
      - nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort
        path

   - sched: act_ct: fix sk_buff leak when the header checks reject a
     packet

   - sctp: clear new_transport when removing a peer

   - dibs: correct freeing of dmb_clientid_arr

   - ovpn: fix NULL dereference when killing missing key

   - eth:
      - veth: fix queue index used to wake the peer txq in veth_poll
      - ngbe: fix NULL pointer dereference in non-MSI-X interrupt
        enabling
      - gve: fix zero-length skb frag with header-split

  Previous releases - always broken:

   - core: fix skb length accounting after generic XDP frag adjustment

   - af_packet: don't send zero-byte data in tpacket_snd().

   - eth:
      - bnxt: avoid deadlock when canceling IRQ affinity notifier
      - ipvlan: inherit needed_headroom and needed_tailroom from
        phy_dev"

* tag 'net-7.2-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (55 commits)
  l2tp: fix tunnel and session refcount leak on seq_file release
  net/sched: cls_bpf: reject dev-bound programs bound to a different device
  sctp: fix use-after-free of cached ASCONF chunk
  net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG
  sctp: clear new_transport when removing a peer
  net/dibs: Correct freeing of dmb_clientid_arr
  net/sched: cls_u32: skip hash tables in u32_bind_class()
  gve: fix NULL dereference due to missing ptp adjfine
  gve: fix zero-length skb frag with header-split
  net/sched: act_api: fix TOCTOU NULL deref on a->goto_chain
  af_packet: Don't send zero-byte data in tpacket_snd().
  tipc: read le->link under the node lock in tipc_node_link_down()
  selftests: tls: cover splice after a failed decrypt
  net/tls: Fail tls_sw_splice_read() after a failed async decrypt
  net: ngbe: fix NULL pointer dereference in non-MSI-X interrupt enabling
  net: tap: fix wrong transport_header when sending VLAN-tagged frame
  net: packet: fix wrong transport_header when sending VLAN-tagged frame
  vxlan: do not arm the ageing timer on a device that is down
  ipv4: fix use-after-free in fib_nhc_update_mtu()
  NTB: ntb_netdev: Preserve RX queue depth on allocation failure
  ...
This commit is contained in:
Linus Torvalds
2026-08-13 08:37:26 -07:00
76 changed files with 893 additions and 326 deletions
+4
View File
@@ -212,6 +212,10 @@ Christophe Ricard <christophe.ricard@gmail.com>
Christopher Obbard <christopher.obbard@linaro.org> <chris.obbard@collabora.com>
Christoph Hellwig <hch@lst.de>
Christoph Manszewski <c.manszewski@gmail.com> <christoph.manszewski@intel.com>
Christoph Paasch <cpaasch@openai.com> <christoph.paasch@gmail.com>
Christoph Paasch <cpaasch@openai.com> <christoph.paasch@student.uclouvain.be>
Christoph Paasch <cpaasch@openai.com> <christoph.paasch@uclouvain.be>
Christoph Paasch <cpaasch@openai.com> <cpaasch@apple.com>
Chuck Lever <cel@kernel.org> <chuck.lever@oracle.com>
Chuck Lever <cel@kernel.org> <cel@netapp.com>
Chuck Lever <cel@kernel.org> <cel@citi.umich.edu>
-1
View File
@@ -335,7 +335,6 @@ static int dibs_lo_dev_probe(void)
return 0;
err_reg:
kfree(dibs->dmb_clientid_arr);
/* pairs with dibs_dev_alloc() */
put_device(&dibs->dev);
kfree(ldev);
+4 -10
View File
@@ -128,6 +128,7 @@ static void dibs_dev_release(struct device *dev)
dibs = container_of(dev, struct dibs_dev, dev);
kfree(dibs->dmb_clientid_arr);
kfree(dibs);
}
@@ -194,12 +195,13 @@ int dibs_dev_add(struct dibs_dev *dibs)
ret = device_add(&dibs->dev);
if (ret)
goto free_client_arr;
return ret;
ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
if (ret) {
dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
goto err_device_del;
device_del(&dibs->dev);
return ret;
}
mutex_lock(&dibs_dev_list.mutex);
mutex_lock(&clients_lock);
@@ -214,13 +216,6 @@ int dibs_dev_add(struct dibs_dev *dibs)
mutex_unlock(&dibs_dev_list.mutex);
return 0;
err_device_del:
device_del(&dibs->dev);
free_client_arr:
kfree(dibs->dmb_clientid_arr);
return ret;
}
EXPORT_SYMBOL_GPL(dibs_dev_add);
@@ -247,7 +242,6 @@ void dibs_dev_del(struct dibs_dev *dibs)
mutex_unlock(&dibs_dev_list.mutex);
device_del(&dibs->dev);
kfree(dibs->dmb_clientid_arr);
}
EXPORT_SYMBOL_GPL(dibs_dev_del);
+88 -41
View File
@@ -11789,10 +11789,21 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify,
{
struct bnxt_irq *irq;
u16 tag;
int err;
irq = container_of(notify, struct bnxt_irq, affinity_notify);
#ifdef CONFIG_RFS_ACCEL
if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) {
int err;
err = cpu_rmap_update(irq->bp->dev->rx_cpu_rmap, irq->ring_nr,
mask);
if (err)
netdev_warn(irq->bp->dev,
"aRFS rmap update failed: %d\n", err);
}
#endif
if (!irq->bp->tph_mode)
return;
@@ -11802,20 +11813,11 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify,
return;
if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM,
cpumask_first(irq->cpu_mask), &tag))
cpumask_first(mask), &tag))
return;
if (pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag))
return;
netdev_lock(irq->bp->dev);
if (netif_running(irq->bp->dev)) {
err = netdev_rx_queue_restart(irq->bp->dev, irq->ring_nr);
if (err)
netdev_err(irq->bp->dev,
"RX queue restart failed: err=%d\n", err);
}
netdev_unlock(irq->bp->dev);
WRITE_ONCE(irq->new_tag, tag);
bnxt_queue_sp_work(irq->bp, BNXT_TPH_UPDATE_SP_EVENT);
}
static void bnxt_irq_affinity_release(struct kref *ref)
@@ -11866,10 +11868,6 @@ static void bnxt_free_irq(struct bnxt *bp)
struct bnxt_irq *irq;
int i;
#ifdef CONFIG_RFS_ACCEL
free_irq_cpu_rmap(bp->dev->rx_cpu_rmap);
bp->dev->rx_cpu_rmap = NULL;
#endif
if (!bp->irq_tbl || !bp->bnapi)
return;
@@ -11878,27 +11876,35 @@ static void bnxt_free_irq(struct bnxt *bp)
irq = &bp->irq_tbl[map_idx];
if (irq->requested) {
bnxt_release_irq_notifier(irq);
if (irq->have_cpumask) {
irq_update_affinity_hint(irq->vector, NULL);
free_cpumask_var(irq->cpu_mask);
irq->have_cpumask = 0;
}
bnxt_release_irq_notifier(irq);
free_irq(irq->vector, bp->bnapi[i]);
}
irq->requested = 0;
irq->tag = 0;
irq->new_tag = 0;
}
/* Disable TPH support */
pcie_disable_tph(bp->pdev);
bp->tph_mode = 0;
#ifdef CONFIG_RFS_ACCEL
free_irq_cpu_rmap(bp->dev->rx_cpu_rmap);
bp->dev->rx_cpu_rmap = NULL;
#endif
}
static int bnxt_request_irq(struct bnxt *bp)
{
const int numa_node = dev_to_node(&bp->pdev->dev);
struct cpu_rmap *rmap = NULL;
int i, j, rc = 0;
unsigned long flags = 0;
@@ -11921,6 +11927,8 @@ static int bnxt_request_irq(struct bnxt *bp)
for (i = 0, j = 0; i < bp->cp_nr_rings; i++) {
int map_idx = bnxt_cp_num_to_irq_num(bp, i);
struct bnxt_irq *irq = &bp->irq_tbl[map_idx];
unsigned int cpu_num;
u16 tag;
if (IS_ENABLED(CONFIG_RFS_ACCEL) &&
rmap && bp->bnapi[i]->rx_ring) {
@@ -11939,32 +11947,31 @@ static int bnxt_request_irq(struct bnxt *bp)
netif_napi_set_irq_locked(&bp->bnapi[i]->napi, irq->vector);
irq->requested = 1;
if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) {
int numa_node = dev_to_node(&bp->pdev->dev);
u16 tag;
if (!zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL))
continue;
irq->have_cpumask = 1;
irq->msix_nr = map_idx;
irq->ring_nr = i;
cpumask_set_cpu(cpumask_local_spread(i, numa_node),
irq->cpu_mask);
rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask);
if (rc) {
netdev_warn(bp->dev,
"Update affinity hint failed, IRQ = %d\n",
irq->vector);
break;
}
irq->have_cpumask = 1;
irq->msix_nr = map_idx;
irq->ring_nr = i;
cpu_num = cpumask_local_spread(i, numa_node);
cpumask_set_cpu(cpu_num, irq->cpu_mask);
bnxt_register_irq_notifier(bp, irq);
/* Init ST table entry if we can get the mapping */
if (!pcie_tph_get_cpu_st(bp->pdev, TPH_MEM_TYPE_VM,
cpu_num, &tag)) {
pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag);
irq->tag = tag;
irq->new_tag = tag;
}
/* Init ST table entry */
if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM,
cpumask_first(irq->cpu_mask),
&tag))
continue;
bnxt_register_irq_notifier(bp, irq);
pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag);
rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask);
if (rc) {
netdev_warn(bp->dev,
"Update affinity hint failed, IRQ = %d\n",
irq->vector);
break;
}
}
return rc;
@@ -14468,6 +14475,43 @@ static void bnxt_rtnl_unlock_sp(struct bnxt *bp)
rtnl_unlock();
}
static void bnxt_tph_update(struct bnxt *bp)
{
struct net_device *dev = bp->dev;
int i;
bnxt_lock_sp(bp);
if (!test_bit(BNXT_STATE_OPEN, &bp->state))
goto unlock;
for (i = 0; i < bp->rx_nr_rings; i++) {
struct bnxt_irq *irq;
int map_idx, err;
u16 tag;
map_idx = bnxt_cp_num_to_irq_num(bp, i);
irq = &bp->irq_tbl[map_idx];
tag = READ_ONCE(irq->new_tag);
if (irq->tag == tag)
continue;
if (pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag))
continue;
err = netdev_rx_queue_restart(dev, irq->ring_nr);
if (err) {
netdev_err(dev, "RX queue restart failed: err=%d\n",
err);
continue;
}
irq->tag = tag;
}
unlock:
bnxt_unlock_sp(bp);
}
/* Only called from bnxt_sp_task() */
static void bnxt_reset(struct bnxt *bp, bool silent)
{
@@ -14892,6 +14936,9 @@ static void bnxt_sp_task(struct work_struct *work)
bnxt_devlink_health_fw_report(bp);
}
if (test_and_clear_bit(BNXT_TPH_UPDATE_SP_EVENT, &bp->sp_event))
bnxt_tph_update(bp);
smp_mb__before_atomic();
clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
}
@@ -1268,6 +1268,8 @@ struct bnxt_irq {
struct bnxt *bp;
int msix_nr;
int ring_nr;
u16 tag;
u16 new_tag;
struct irq_affinity_notify affinity_notify;
};
@@ -2642,6 +2644,7 @@ struct bnxt {
#define BNXT_RING_COAL_NOW_SP_EVENT 17
#define BNXT_FW_RESET_NOTIFY_SP_EVENT 18
#define BNXT_FW_EXCEPTION_SP_EVENT 19
#define BNXT_TPH_UPDATE_SP_EVENT 20
#define BNXT_LINK_CFG_CHANGE_SP_EVENT 21
#define BNXT_THERMAL_THRESHOLD_SP_EVENT 22
#define BNXT_FW_ECHO_REQUEST_SP_EVENT 23
@@ -26,6 +26,11 @@ int gve_clock_nic_ts_read(struct gve_priv *priv)
return 0;
}
static int gve_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
return -EOPNOTSUPP;
}
static int gve_ptp_gettimex64(struct ptp_clock_info *info,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
@@ -60,6 +65,7 @@ out:
static const struct ptp_clock_info gve_ptp_caps = {
.owner = THIS_MODULE,
.name = "gve clock",
.adjfine = gve_ptp_adjfine,
.gettimex64 = gve_ptp_gettimex64,
.settime64 = gve_ptp_settime64,
.do_aux_work = gve_ptp_do_aux_work,
@@ -886,6 +886,11 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
rx->rx_hsplit_unsplit_pkt += unsplit;
rx->rx_hsplit_bytes += hdr_len;
u64_stats_update_end(&rx->statss);
if (!buf_len) {
gve_free_buffer(rx, buf_state);
return 0;
}
} else if (!rx->ctx.skb_head && rx->dqo.page_pool &&
netmem_is_net_iov(buf_state->page_info.netmem)) {
/* when header split is disabled, the header went to the packet
+2
View File
@@ -1310,6 +1310,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_rx_flow *flow,
k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma);
pkt_len = cppi5_hdesc_get_pktlen(desc_rx);
cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
/* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */
port_id &= 0xFF;
dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id);
port = am65_common_get_port(common, port_id);
ndev = port->ndev;
@@ -180,8 +180,10 @@ static void ngbe_irq_enable(struct wx *wx, bool queues)
/* mask interrupt */
if (queues)
wx_intr_enable(wx, NGBE_INTR_ALL);
else
else if (wx->pdev->msix_enabled)
wx_intr_enable(wx, NGBE_INTR_MISC(wx));
else
wx_intr_enable(wx, BIT(0));
}
/**
+4
View File
@@ -146,6 +146,8 @@ static int ipvlan_init(struct net_device *dev)
dev->lltx = true;
netif_inherit_tso_max(dev, phy_dev);
dev->hard_header_len = phy_dev->hard_header_len;
dev->needed_headroom = phy_dev->needed_headroom;
dev->needed_tailroom = phy_dev->needed_tailroom;
netdev_lockdep_set_classes(dev);
@@ -773,6 +775,8 @@ static int ipvlan_device_event(struct notifier_block *unused,
case NETDEV_FEAT_CHANGE:
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
netif_inherit_tso_max(ipvlan->dev, dev);
ipvlan->dev->needed_headroom = dev->needed_headroom;
ipvlan->dev->needed_tailroom = dev->needed_tailroom;
netdev_update_features(ipvlan->dev);
}
break;
+4
View File
@@ -950,6 +950,8 @@ static int macvlan_init(struct net_device *dev)
dev->lltx = true;
netif_inherit_tso_max(dev, lowerdev);
dev->hard_header_len = lowerdev->hard_header_len;
dev->needed_headroom = lowerdev->needed_headroom;
dev->needed_tailroom = lowerdev->needed_tailroom;
macvlan_set_lockdep_class(dev);
vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
@@ -1824,6 +1826,8 @@ static int macvlan_device_event(struct notifier_block *unused,
case NETDEV_FEAT_CHANGE:
list_for_each_entry(vlan, &port->vlans, list) {
netif_inherit_tso_max(vlan->dev, dev);
vlan->dev->needed_headroom = dev->needed_headroom;
vlan->dev->needed_tailroom = dev->needed_tailroom;
netdev_update_features(vlan->dev);
}
break;
+8 -7
View File
@@ -127,8 +127,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
{
struct ntb_netdev_queue *q = qp_data;
struct ntb_netdev *dev = q->ntdev;
struct sk_buff *skb, *new_skb;
struct net_device *ndev;
struct sk_buff *skb;
int rc;
ndev = dev->ndev;
@@ -144,6 +144,12 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
goto enqueue_again;
}
new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
if (!new_skb) {
ndev->stats.rx_dropped++;
goto enqueue_again;
}
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, ndev);
skb->ip_summed = CHECKSUM_NONE;
@@ -157,12 +163,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
ndev->stats.rx_bytes += len;
}
skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
if (!skb) {
ndev->stats.rx_errors++;
ndev->stats.rx_frame_errors++;
return;
}
skb = new_skb;
enqueue_again:
rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN);
+11 -15
View File
@@ -18,20 +18,12 @@
#include "crypto_aead.h"
#include "crypto.h"
static void ovpn_ks_destroy_rcu(struct rcu_head *head)
{
struct ovpn_crypto_key_slot *ks;
ks = container_of(head, struct ovpn_crypto_key_slot, rcu);
ovpn_aead_crypto_key_slot_destroy(ks);
}
void ovpn_crypto_key_slot_release(struct kref *kref)
{
struct ovpn_crypto_key_slot *ks;
ks = container_of(kref, struct ovpn_crypto_key_slot, refcount);
call_rcu(&ks->rcu, ovpn_ks_destroy_rcu);
queue_rcu_work(ovpn_wq, &ks->free_work);
}
/* can only be invoked when all peer references have been dropped (i.e. RCU
@@ -58,15 +50,19 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs)
bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id)
{
struct ovpn_crypto_key_slot *ks = NULL;
struct ovpn_crypto_key_slot *tmp;
int slot = 0;
spin_lock_bh(&cs->lock);
if (rcu_access_pointer(cs->slots[0])->key_id == key_id) {
ks = rcu_replace_pointer(cs->slots[0], NULL,
lockdep_is_held(&cs->lock));
} else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) {
ks = rcu_replace_pointer(cs->slots[1], NULL,
lockdep_is_held(&cs->lock));
tmp = rcu_access_pointer(cs->slots[slot]);
if (!tmp || tmp->key_id != key_id) {
slot = 1;
tmp = rcu_access_pointer(cs->slots[slot]);
}
if (tmp && tmp->key_id == key_id)
ks = rcu_replace_pointer(cs->slots[slot], NULL,
lockdep_is_held(&cs->lock));
spin_unlock_bh(&cs->lock);
if (ks)
+3 -1
View File
@@ -10,6 +10,8 @@
#ifndef _NET_OVPN_OVPNCRYPTO_H_
#define _NET_OVPN_OVPNCRYPTO_H_
#include <linux/workqueue.h>
#include "pktid.h"
#include "proto.h"
@@ -45,8 +47,8 @@ struct ovpn_crypto_key_slot {
struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp;
struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp;
struct rcu_work free_work;
struct kref refcount;
struct rcu_head rcu;
};
struct ovpn_crypto_state {
+14 -5
View File
@@ -9,6 +9,7 @@
#include <crypto/aead.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/udp.h>
@@ -380,13 +381,19 @@ error:
return ERR_PTR(ret);
}
void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks)
static void ovpn_aead_crypto_key_slot_free(struct ovpn_crypto_key_slot *ks)
{
if (!ks)
return;
crypto_free_aead(ks->encrypt);
crypto_free_aead(ks->decrypt);
}
static void ovpn_aead_crypto_key_slot_free_work(struct work_struct *work)
{
struct ovpn_crypto_key_slot *ks;
ks = container_of(to_rcu_work(work), struct ovpn_crypto_key_slot,
free_work);
ovpn_aead_crypto_key_slot_free(ks);
kfree(ks);
}
@@ -420,6 +427,7 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
ks->encrypt = NULL;
ks->decrypt = NULL;
INIT_RCU_WORK(&ks->free_work, ovpn_aead_crypto_key_slot_free_work);
kref_init(&ks->refcount);
ks->key_id = kc->key_id;
@@ -453,7 +461,8 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
return ks;
destroy_ks:
ovpn_aead_crypto_key_slot_destroy(ks);
ovpn_aead_crypto_key_slot_free(ks);
kfree(ks);
return ERR_PTR(ret);
}
-1
View File
@@ -22,7 +22,6 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
struct ovpn_crypto_key_slot *
ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc);
void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);
enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks);
+5 -5
View File
@@ -204,10 +204,10 @@ drop:
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
kfree_skb(skb);
drop_nocount:
if (likely(peer))
ovpn_peer_put(peer);
if (likely(ks))
ovpn_crypto_key_slot_put(ks);
if (likely(peer))
ovpn_peer_put(peer);
}
/* RX path entry point: decrypt packet and forward it to the device */
@@ -302,11 +302,11 @@ err_unlock:
err:
if (unlikely(skb))
ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
if (likely(peer))
ovpn_peer_put(peer);
kfree_skb(skb);
if (likely(ks))
ovpn_crypto_key_slot_put(ks);
kfree_skb(skb);
if (likely(peer))
ovpn_peer_put(peer);
}
static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
+18 -1
View File
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/workqueue.h>
#include <net/gro_cells.h>
#include <net/ip.h>
#include <net/rtnetlink.h>
@@ -26,6 +27,9 @@
#include "tcp.h"
#include "udp.h"
/* module-owned workqueue on which all ovpn-specific work is queued */
struct workqueue_struct *ovpn_wq;
static void ovpn_priv_free(struct net_device *net)
{
struct ovpn_priv *ovpn = netdev_priv(net);
@@ -264,10 +268,16 @@ static int __init ovpn_init(void)
ovpn_tcp_init();
ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0);
if (!ovpn_wq) {
pr_err("ovpn: cannot allocate workqueue\n");
return -ENOMEM;
}
err = rtnl_link_register(&ovpn_link_ops);
if (err) {
pr_err("ovpn: can't register rtnl link ops: %d\n", err);
return err;
goto destroy_wq;
}
err = ovpn_nl_register();
@@ -280,6 +290,9 @@ static int __init ovpn_init(void)
unreg_rtnl:
rtnl_link_unregister(&ovpn_link_ops);
destroy_wq:
destroy_workqueue(ovpn_wq);
ovpn_wq = NULL;
return err;
}
@@ -288,7 +301,11 @@ static __exit void ovpn_cleanup(void)
ovpn_nl_unregister();
rtnl_link_unregister(&ovpn_link_ops);
flush_workqueue(ovpn_wq);
rcu_barrier();
destroy_workqueue(ovpn_wq);
ovpn_wq = NULL;
}
module_init(ovpn_init);
+4
View File
@@ -15,6 +15,10 @@
#include <uapi/linux/if_link.h>
#include <uapi/linux/ovpn.h>
struct workqueue_struct;
extern struct workqueue_struct *ovpn_wq;
/**
* struct ovpn_peer_collection - container of peers for MultiPeer mode
* @by_id: table of peers index by ID
+4 -4
View File
@@ -62,7 +62,7 @@ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
/* now that interval and timeout have been changed, kick
* off the worker so that the next delay can be recomputed
*/
mod_delayed_work(system_percpu_wq, &peer->ovpn->keepalive_work, 0);
mod_delayed_work(ovpn_wq, &peer->ovpn->keepalive_work, 0);
}
/**
@@ -1371,7 +1371,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
peer->id);
if (WARN_ON(!ovpn_peer_hold(peer)))
return 0;
if (!schedule_work(&peer->keepalive_work))
if (!queue_work(ovpn_wq, &peer->keepalive_work))
ovpn_peer_put(peer);
}
@@ -1463,8 +1463,8 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
netdev_dbg(ovpn->dev,
"scheduling keepalive work: now=%llu next_run=%llu delta=%llu\n",
next_run, now, next_run - now);
schedule_delayed_work(&ovpn->keepalive_work,
(next_run - now) * HZ);
queue_delayed_work(ovpn_wq, &ovpn->keepalive_work,
(next_run - now) * HZ);
}
unlock_ovpn(ovpn, &release_list);
}
+4 -5
View File
@@ -151,7 +151,7 @@ err:
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
if (!schedule_work(&peer->tcp.defer_del_work))
if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
ovpn_peer_put(peer);
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
@@ -284,13 +284,12 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
* stream therefore we abort the connection
*/
ovpn_peer_hold(peer);
if (!schedule_work(&peer->tcp.defer_del_work))
if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
ovpn_peer_put(peer);
/* we bail out immediately and keep tx_in_progress set
* to true. This way we prevent more TX attempts
* which would lead to more invocations of
* schedule_work()
* which would lead to more invocations of queue_work()
*/
return;
}
@@ -487,7 +486,7 @@ static void ovpn_tcp_write_space(struct sock *sk)
rcu_read_lock();
sock = rcu_dereference_sk_user_data(sk);
if (likely(sock && sock->peer)) {
schedule_work(&sock->tcp_tx_work);
queue_work(ovpn_wq, &sock->tcp_tx_work);
sock->peer->tcp.sk_cb.sk_write_space(sk);
}
rcu_read_unlock();
+1 -1
View File
@@ -1370,7 +1370,7 @@ static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
if (devnum == MDIO_MMD_VEND2)
ret = rtlgen_write_vend2(phydev, regnum, val);
else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV)
ret = rtlgen_write_vend2(phydev, regnum, RTL_MDIO_AN_EEE_ADV);
ret = rtlgen_write_vend2(phydev, RTL_MDIO_AN_EEE_ADV, val);
else
ret = -EOPNOTSUPP;
+2 -1
View File
@@ -1091,12 +1091,13 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
}
}
skb_probe_transport_header(skb);
/* Move network header to the right position for VLAN tagged packets */
if (eth_type_vlan(skb->protocol) &&
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
skb_probe_transport_header(skb);
dev_queue_xmit(skb);
rcu_read_unlock();
+15 -9
View File
@@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
skb_reset_mac_header(skb);
/* check if bpf_xdp_adjust_tail was used */
off = xdp->data_end - orig_data_end;
if (off != 0)
__skb_put(skb, off); /* positive on grow, negative on shrink */
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
* (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
* from skb->len before updating data_len, then add the new one back.
*/
if (xdp_buff_has_frags(xdp))
skb->len -= skb->data_len;
if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
else
skb->len += skb->data_len;
} else {
skb->data_len = 0;
}
/* Synchronize the skb tail with XDP's updated linear area. */
off = xdp->data_end - orig_data_end;
if (off != 0) {
skb_set_tail_pointer(skb, xdp->data_end - xdp->data);
skb->len += off; /* positive on grow, negative on shrink */
}
skb->protocol = eth_type_trans(skb, rq->dev);
@@ -961,7 +967,7 @@ static int veth_poll(struct napi_struct *napi, int budget)
struct veth_rq *rq =
container_of(napi, struct veth_rq, xdp_napi);
struct veth_priv *priv = netdev_priv(rq->dev);
int queue_idx = rq->xdp_rxq.queue_index;
int queue_idx = rq - priv->rq;
struct netdev_queue *peer_txq;
struct veth_stats stats = {};
struct net_device *peer_dev;
+1 -1
View File
@@ -4500,7 +4500,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (change_igmp && vxlan_addr_multicast(&dst->remote_ip))
err = vxlan_multicast_leave(vxlan);
if (conf.age_interval != vxlan->cfg.age_interval)
if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval)
mod_timer(&vxlan->age_timer, jiffies);
netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
+19
View File
@@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
struct tcf_chain *newchain);
/* Range check for a control action supplied by user space.
*
* This is the same test tcf_action_check_ctrlact() applies to the primary
* control action, factored out for the *fallback* control actions
* (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT),
* which must not reach tcf_action_check_ctrlact() because they have no
* goto_chain to allocate. Without it, user space can store kernel-internal
* verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is
* deliberately not part of the UAPI value range.
*/
static inline bool tcf_action_valid(int action)
{
int opcode = TC_ACT_EXT_OPCODE(action);
if (!opcode)
return action <= TC_ACT_VALUE_MAX;
return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC;
}
#ifdef CONFIG_INET
DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
#endif
+23 -6
View File
@@ -36,6 +36,12 @@
#define IP_VS_HDR_INVERSE 1
#define IP_VS_HDR_ICMP 2
/* Destination Server Flags */
#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
/* Destination Server Config Flags */
#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */
/* conn_tab limits (as per Kconfig) */
#define IP_VS_CONN_TAB_MIN_BITS 8
#if BITS_PER_LONG > 32
@@ -976,6 +982,7 @@ struct ip_vs_dest {
volatile unsigned int flags; /* dest status flags */
atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
unsigned long cflags; /* config flags */
atomic_t last_weight; /* server latest weight */
__u16 tun_type; /* tunnel type */
__be16 tun_port; /* tunnel port */
@@ -987,10 +994,11 @@ struct ip_vs_dest {
/* connection counters and thresholds */
atomic_t activeconns; /* active connections */
atomic_t inactconns; /* inactive connections */
atomic_t totalconns; /* total connections */
atomic_t persistconns; /* persistent connections */
__u32 u_threshold; /* upper threshold */
__u32 l_threshold; /* lower threshold */
__u32 l_threshold_val;/* used lower threshold */
/* for destination cache */
spinlock_t dst_lock; /* lock of dst_cache */
@@ -1907,6 +1915,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
kfree(dest);
}
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode);
/* IPVS sync daemon data and function prototypes
* (from ip_vs_sync.c)
*/
@@ -2058,7 +2068,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
!(cp->flags & IP_VS_CONN_F_TEMPLATE);
}
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int dir, unsigned int toff,
bool has_ports, struct ip_vs_iphdr *ciph);
@@ -2220,14 +2230,21 @@ void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af);
static inline int
ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
{
/* We think the overhead of processing active connections is 256
/* We think the overhead of processing active connections is 257
* times higher than that of inactive connections in average. (This
* 256 times might not be accurate, we will change it later) We
* 257 times might not be accurate, we will change it later) We
* use the following formula to estimate the overhead now:
* dest->activeconns*256 + dest->inactconns
* dest->activeconns*256 + dest->totalconns
*/
return (atomic_read(&dest->activeconns) << 8) +
atomic_read(&dest->inactconns);
atomic_read(&dest->totalconns);
}
static inline int
ip_vs_dest_inactconns(const struct ip_vs_dest *dest)
{
return max(atomic_read(&dest->totalconns) -
atomic_read(&dest->activeconns), 0);
}
#ifdef CONFIG_IP_VS_PROTO_TCP
+2
View File
@@ -276,6 +276,8 @@ int fib_dump_info_fnhe(struct sk_buff *skb, struct netlink_callback *cb,
u32 table_id, struct fib_info *fi,
int *fa_index, int fa_start, unsigned int flags);
void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig);
static inline void ip_rt_put(struct rtable *rt)
{
/* dst_release() accepts a NULL parameter.
+3
View File
@@ -22,6 +22,7 @@ struct sctp_endpoint;
struct sctp_association;
struct sctp_authkey;
struct sctp_hmacalgo;
struct sctp_cookie;
/* Defines an HMAC algorithm supported by SCTP chunk authentication */
struct sctp_hmac {
@@ -72,6 +73,8 @@ struct sctp_shared_key *sctp_auth_get_shkey(
int sctp_auth_asoc_copy_shkeys(const struct sctp_endpoint *ep,
struct sctp_association *asoc,
gfp_t gfp);
bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep,
const struct sctp_cookie *cookie);
const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id);
const struct sctp_hmac *
sctp_auth_asoc_get_hmac(const struct sctp_association *asoc);
-6
View File
@@ -28,12 +28,6 @@
#define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */
#define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */
/*
* Destination Server Flags
*/
#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */
#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
/*
* IPVS sync daemon states
*/
@@ -280,6 +280,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
ret = nf_ct_br_defrag6(skb, &bridge_state);
break;
default:
nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
return NF_ACCEPT;
}
+7 -3
View File
@@ -5517,12 +5517,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
}
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
* (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
* from skb->len before updating data_len, then add the new one back.
*/
if (xdp_buff_has_frags(xdp))
skb->len -= skb->data_len;
if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
else
skb->len += skb->data_len;
} else {
skb->data_len = 0;
}
/* check if XDP changed eth hdr such SKB needs update */
eth = (struct ethhdr *)xdp->data;
+1 -1
View File
@@ -40,7 +40,7 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev,
struct netdev_rx_queue *hw_rxq;
struct device *dma_dev;
netdev_assert_locked_ops_compat(dev);
netdev_assert_locked(dev);
/* Only RX side supports queue leasing today. */
if (type != NETDEV_QUEUE_TYPE_RX || !netif_rxq_is_leased(dev, idx))
+11 -23
View File
@@ -1895,42 +1895,30 @@ static int call_fib_nh_notifiers(struct fib_nh *nh,
return NOTIFY_DONE;
}
/* Update the PMTU of exceptions when:
* - the new MTU of the first hop becomes smaller than the PMTU
* - the old MTU was the same as the PMTU, and it limited discovery of
* larger MTUs on the path. With that limit raised, we can now
* discover larger MTUs
* A special case is locked exceptions, for which the PMTU is smaller
* than the minimal accepted PMTU:
* - if the new MTU is greater than the PMTU, don't make any change
* - otherwise, unlock and set PMTU
/* Walk the exceptions of a nexthop after its first hop MTU changed. The
* chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock
* for the update of each entry.
*/
void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig)
{
struct fnhe_hash_bucket *bucket;
int i;
bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1);
rcu_read_lock();
bucket = rcu_dereference(nhc->nhc_exceptions);
if (!bucket)
return;
goto out;
for (i = 0; i < FNHE_HASH_SIZE; i++) {
struct fib_nh_exception *fnhe;
for (fnhe = rcu_dereference_protected(bucket[i].chain, 1);
for (fnhe = rcu_dereference(bucket[i].chain);
fnhe;
fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) {
if (fnhe->fnhe_mtu_locked) {
if (new <= fnhe->fnhe_pmtu) {
fnhe->fnhe_pmtu = new;
fnhe->fnhe_mtu_locked = false;
}
} else if (new < fnhe->fnhe_pmtu ||
orig == fnhe->fnhe_pmtu) {
fnhe->fnhe_pmtu = new;
}
}
fnhe = rcu_dereference(fnhe->fnhe_next))
fnhe_update_pmtu(fnhe, new, orig);
}
out:
rcu_read_unlock();
}
void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
+29
View File
@@ -741,6 +741,35 @@ out_unlock:
spin_unlock_bh(&fnhe_lock);
}
/* Update the PMTU of an exception when:
* - the new MTU of the first hop becomes smaller than the PMTU
* - the old MTU was the same as the PMTU, and it limited discovery of
* larger MTUs on the path. With that limit raised, we can now
* discover larger MTUs
* A special case is locked exceptions, for which the PMTU is smaller
* than the minimal accepted PMTU:
* - if the new MTU is greater than the PMTU, don't make any change
* - otherwise, unlock and set PMTU
*
* fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against
* update_or_create_fnhe(), which sets both under the same lock.
*/
void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig)
{
spin_lock_bh(&fnhe_lock);
if (fnhe->fnhe_mtu_locked) {
if (new <= fnhe->fnhe_pmtu) {
fnhe->fnhe_pmtu = new;
fnhe->fnhe_mtu_locked = false;
}
} else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) {
fnhe->fnhe_pmtu = new;
}
spin_unlock_bh(&fnhe_lock);
}
static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4,
bool kill_route)
{
+3 -3
View File
@@ -1039,9 +1039,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
/* The fastest case is the first. */
icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;
} else if (m < icsk->icsk_ack.ato) {
icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;
if (icsk->icsk_ack.ato > icsk->icsk_rto)
icsk->icsk_ack.ato = icsk->icsk_rto;
icsk->icsk_ack.ato = min3((icsk->icsk_ack.ato >> 1) + (u32)m,
icsk->icsk_rto,
(u32)TCP_DELACK_MAX);
} else if (m > icsk->icsk_rto) {
/* Too long gap. Apparently sender failed to
* restart window, so that we send ACKs quickly.
+3 -1
View File
@@ -334,7 +334,9 @@ void tcp_delack_timer_handler(struct sock *sk)
if (inet_csk_ack_scheduled(sk)) {
if (!inet_csk_in_pingpong_mode(sk)) {
/* Delayed ACK missed: inflate ATO. */
icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto);
icsk->icsk_ack.ato = min3((u32)icsk->icsk_ack.ato << 1,
icsk->icsk_rto,
(u32)TCP_DELACK_MAX);
} else {
/* Delayed ACK missed: leave pingpong mode and
* deflate ATO.
+4
View File
@@ -306,6 +306,10 @@ static int l2tp_dfs_seq_release(struct inode *inode, struct file *file)
seq = file->private_data;
pd = seq->private;
if (pd->session)
l2tp_session_put(pd->session);
if (pd->tunnel)
l2tp_tunnel_put(pd->tunnel);
if (pd->net)
put_net_track(pd->net, &pd->ns_tracker);
kfree(pd);
+53 -3
View File
@@ -1597,7 +1597,53 @@ static const struct seq_operations pppol2tp_seq_ops = {
.stop = pppol2tp_seq_stop,
.show = pppol2tp_seq_show,
};
#endif /* CONFIG_PROC_FS */
static int pppol2tp_proc_open(struct inode *inode, struct file *file)
{
struct net *net = pde_data(inode);
struct pppol2tp_seq_data *pd;
net = maybe_get_net(net);
if (!net)
return -ENXIO;
pd = __seq_open_private(file, &pppol2tp_seq_ops, sizeof(*pd));
if (!pd) {
put_net(net);
return -ENOMEM;
}
#ifdef CONFIG_NET_NS
pd->p.net = net;
netns_tracker_alloc(net, &pd->p.ns_tracker, GFP_KERNEL);
#endif
return 0;
}
static int pppol2tp_proc_release(struct inode *inode, struct file *file)
{
struct seq_file *seq = file->private_data;
struct pppol2tp_seq_data *pd = seq->private;
if (pd->session)
l2tp_session_put(pd->session);
if (pd->tunnel)
l2tp_tunnel_put(pd->tunnel);
#ifdef CONFIG_NET_NS
put_net_track(pd->p.net, &pd->p.ns_tracker);
#else
put_net(&init_net);
#endif
return seq_release_private(inode, file);
}
static const struct proc_ops pppol2tp_proc_ops = {
.proc_open = pppol2tp_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = pppol2tp_proc_release,
};
/*****************************************************************************
* Network namespace
@@ -1608,8 +1654,8 @@ static __net_init int pppol2tp_init_net(struct net *net)
struct proc_dir_entry *pde;
int err = 0;
pde = proc_create_net("pppol2tp", 0444, net->proc_net,
&pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data));
pde = proc_create_data("pppol2tp", 0444, net->proc_net,
&pppol2tp_proc_ops, net);
if (!pde) {
err = -ENOMEM;
goto out;
@@ -1624,9 +1670,13 @@ static __net_exit void pppol2tp_exit_net(struct net *net)
remove_proc_entry("pppol2tp", net->proc_net);
}
#endif /* CONFIG_PROC_FS */
static struct pernet_operations pppol2tp_net_ops = {
#ifdef CONFIG_PROC_FS
.init = pppol2tp_init_net,
.exit = pppol2tp_exit_net,
#endif
};
/*****************************************************************************
+1 -1
View File
@@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set)
mtype_ext_cleanup(set);
bitmap_zero(map->members, map->elements);
set->elements = 0;
atomic64_set(&set->ext_size, 0);
DEBUG_NET_WARN_ON_ONCE(atomic64_read(&set->ext_size) > 0);
}
/* Calculate the actual memory size of the set data */
+13 -6
View File
@@ -679,12 +679,19 @@ __ip_set_get(struct ip_set *set)
write_unlock_bh(&ip_set_ref_lock);
}
static void
__ip_set_put_locked(struct ip_set *set)
{
lockdep_assert_held(&ip_set_ref_lock);
BUG_ON(set->ref == 0);
set->ref--;
}
static void
__ip_set_put(struct ip_set *set)
{
write_lock_bh(&ip_set_ref_lock);
BUG_ON(set->ref == 0);
set->ref--;
__ip_set_put_locked(set);
write_unlock_bh(&ip_set_ref_lock);
}
@@ -855,11 +862,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
{
struct ip_set *set;
rcu_read_lock();
set = rcu_dereference(inst->ip_set_list)[index];
write_lock_bh(&ip_set_ref_lock);
set = ip_set(inst, index);
if (set)
__ip_set_put(set);
rcu_read_unlock();
__ip_set_put_locked(set);
write_unlock_bh(&ip_set_ref_lock);
}
void
+6 -4
View File
@@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
e->set = set;
INIT_LIST_HEAD(&e->list);
list_set_init_extensions(set, ext, e);
if (n)
if (n) {
list_set_replace(set, e, n);
else if (next)
return 0;
}
if (next)
list_add_tail_rcu(&e->list, &next->list);
else if (prev)
list_add_rcu(&e->list, &prev->list);
@@ -420,8 +423,7 @@ list_set_flush(struct ip_set *set)
list_for_each_entry_safe(e, n, &map->members, list)
list_set_del(set, e);
set->elements = 0;
atomic64_set(&set->ext_size, 0);
DEBUG_NET_WARN_ON_ONCE(set->elements > 0);
}
static void
+14 -31
View File
@@ -1081,12 +1081,6 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp)
#endif
static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest)
{
return atomic_read(&dest->activeconns)
+ atomic_read(&dest->inactconns);
}
/*
* Bind a connection entry with a virtual service destination
* Called just after a new connection entry is created.
@@ -1147,23 +1141,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
/* Update the connection counters */
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
int tc;
/* It is a normal connection, so modify the counters
* according to the flags, later the protocol can
* update them on state change
*/
if (!(flags & IP_VS_CONN_F_INACTIVE))
atomic_inc(&dest->activeconns);
else
atomic_inc(&dest->inactconns);
tc = atomic_inc_return(&dest->totalconns);
if (tc == READ_ONCE(dest->u_threshold))
ip_vs_dest_update_overload(dest, 1);
} else {
/* It is a persistent connection/template, so increase
the persistent connection counter */
atomic_inc(&dest->persistconns);
}
if (dest->u_threshold != 0 &&
ip_vs_dest_totalconns(dest) >= dest->u_threshold)
dest->flags |= IP_VS_DEST_F_OVERLOAD;
}
@@ -1244,30 +1237,20 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
/* Update the connection counters */
if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
/* It is a normal connection, so decrease the inactconns
or activeconns counter */
if (cp->flags & IP_VS_CONN_F_INACTIVE) {
atomic_dec(&dest->inactconns);
} else {
int tc;
/* It is a normal connection, so decrease the counters */
if (!(cp->flags & IP_VS_CONN_F_INACTIVE))
atomic_dec(&dest->activeconns);
}
tc = atomic_fetch_dec(&dest->totalconns);
if (tc == READ_ONCE(dest->l_threshold_val))
ip_vs_dest_update_overload(dest, -1);
} else {
/* It is a persistent connection/template, so decrease
the persistent connection counter */
atomic_dec(&dest->persistconns);
}
if (dest->l_threshold != 0) {
if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
} else if (dest->u_threshold != 0) {
if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
} else {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
}
ip_vs_dest_put(dest);
}
@@ -1296,7 +1279,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest)
* Checking the dest server status.
*/
if ((dest == NULL) ||
!(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
!(dest->cflags & IP_VS_DEST_CF_AVAILABLE) ||
expire_quiescent_template(ipvs, dest) ||
(cdest && (dest != cdest))) {
IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -2037,7 +2020,7 @@ repeat:
cp = ip_vs_hn0_to_conn(hn);
resched_score++;
dest = cp->dest;
if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE))
continue;
if (atomic_read(&cp->n_control))
+13 -5
View File
@@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -923,7 +923,7 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
* Packet has been made sufficiently writable in caller
* - inout: 1=in->out, 0=out->in
*/
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout, unsigned int toff,
bool has_ports, struct ip_vs_iphdr *ciph)
{
@@ -931,6 +931,11 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff);
struct iphdr *cih = (struct iphdr *)(icmph + 1);
/* Before now we may used ihl from skb frag, revalidate it after
* copying it into skb head to prevent out-of-bounds access
*/
if (cih->ihl * 4 != ciph->len - ciph->off)
return false;
if (inout) {
iph->saddr = cp->vaddr.ip;
ip_send_check(iph);
@@ -964,6 +969,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
else
IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
"Forwarding altered incoming ICMP");
return true;
}
#ifdef CONFIG_IP_VS_IPV6
@@ -1055,7 +1061,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
else
#endif
ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph);
if (!ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph))
goto out;
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
goto out;
@@ -1950,6 +1957,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (pskb_pull(skb, offset2) == NULL)
goto ignore_tunnel;
skb_reset_network_header(skb);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
/* Ensure the IP header is present in headroom */
if (!pskb_may_pull(skb, hlen_orig))
goto ignore_tunnel;
@@ -2210,7 +2218,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
}
/* Check the server status */
if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
/* the destination server is not available */
if (sysctl_expire_nodest_conn(ipvs)) {
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
+66 -15
View File
@@ -1304,6 +1304,40 @@ void ip_vs_stats_free(struct ip_vs_stats *stats)
}
}
/* Update overload flag based on number of dest conns and lower/upper
* connection thresholds:
* - conns reach u_threshold and exceed it: set the flag
* - conns go below l_threshold (or 75% of u_threshold): clear the flag
*/
static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
{
int conns;
u32 l, u;
lockdep_assert_held(&dest->dst_lock);
u = READ_ONCE(dest->u_threshold);
if (!u)
goto unset;
l = READ_ONCE(dest->l_threshold_val);
conns = atomic_read(&dest->totalconns);
if (conns >= (mode > 0 ? l : u)) {
dest->flags |= IP_VS_DEST_F_OVERLOAD;
return;
}
if (conns >= (mode < 0 ? u : l))
return;
unset:
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
}
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
{
spin_lock_bh(&dest->dst_lock);
__ip_vs_dest_update_overload(dest, mode);
spin_unlock_bh(&dest->dst_lock);
}
/*
* Update a destination in the given service
*/
@@ -1368,12 +1402,21 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
}
/* set the dest status flags */
dest->flags |= IP_VS_DEST_F_AVAILABLE;
dest->cflags |= IP_VS_DEST_CF_AVAILABLE;
if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
dest->u_threshold = udest->u_threshold;
dest->l_threshold = udest->l_threshold;
if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
READ_ONCE(dest->l_threshold) != udest->l_threshold) {
spin_lock_bh(&dest->dst_lock);
WRITE_ONCE(dest->u_threshold, udest->u_threshold);
WRITE_ONCE(dest->l_threshold, udest->l_threshold);
/* Low threshold defaults to 75% of upper threshold */
WRITE_ONCE(dest->l_threshold_val,
udest->l_threshold ? :
(udest->u_threshold -
(udest->u_threshold >> 2)));
__ip_vs_dest_update_overload(dest, 0);
spin_unlock_bh(&dest->dst_lock);
}
dest->af = udest->af;
@@ -1445,7 +1488,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
dest->port = udest->port;
atomic_set(&dest->activeconns, 0);
atomic_set(&dest->inactconns, 0);
atomic_set(&dest->totalconns, 0);
atomic_set(&dest->persistconns, 0);
refcount_set(&dest->refcnt, 1);
@@ -1486,6 +1529,9 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
return -ERANGE;
}
if (udest->u_threshold > INT_MAX)
return -EINVAL;
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
if (udest->tun_port == 0) {
pr_err("%s(): tunnel port is zero\n", __func__);
@@ -1559,6 +1605,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
return -ERANGE;
}
if (udest->u_threshold > INT_MAX)
return -EINVAL;
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
if (udest->tun_port == 0) {
pr_err("%s(): tunnel port is zero\n", __func__);
@@ -1613,7 +1662,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
struct ip_vs_dest *dest,
int svcupd)
{
dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE;
spin_lock_bh(&dest->dst_lock);
__ip_vs_dst_cache_reset(dest);
@@ -3031,7 +3080,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
atomic_read(&dest->inactconns));
ip_vs_dest_inactconns(dest));
else
#endif
seq_printf(seq,
@@ -3042,7 +3091,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
atomic_read(&dest->inactconns));
ip_vs_dest_inactconns(dest));
}
}
@@ -3667,10 +3716,10 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests *
entry.port = dest->port;
entry.conn_flags = atomic_read(&dest->conn_flags);
entry.weight = atomic_read(&dest->weight);
entry.u_threshold = dest->u_threshold;
entry.l_threshold = dest->l_threshold;
entry.u_threshold = READ_ONCE(dest->u_threshold);
entry.l_threshold = READ_ONCE(dest->l_threshold);
entry.activeconns = atomic_read(&dest->activeconns);
entry.inactconns = atomic_read(&dest->inactconns);
entry.inactconns = ip_vs_dest_inactconns(dest);
entry.persistconns = atomic_read(&dest->persistconns);
ip_vs_copy_stats(&kstats, &dest->stats);
ip_vs_export_stats_user(&entry.stats, &kstats);
@@ -4277,12 +4326,14 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
dest->tun_port) ||
nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS,
dest->tun_flags) ||
nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) ||
nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) ||
nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH,
READ_ONCE(dest->u_threshold)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH,
READ_ONCE(dest->l_threshold)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
atomic_read(&dest->activeconns)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS,
atomic_read(&dest->inactconns)) ||
ip_vs_dest_inactconns(dest)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
atomic_read(&dest->persistconns)) ||
nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af))
+2 -2
View File
@@ -219,8 +219,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
s = (struct ip_vs_dh_state *) svc->sched_data;
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
if (!dest
|| !(dest->flags & IP_VS_DEST_F_AVAILABLE)
if (!dest ||
!(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
|| atomic_read(&dest->weight) <= 0
|| is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
+1 -1
View File
@@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
dest = en->dest;
if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) &&
atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
goto out;
}
+4 -4
View File
@@ -169,8 +169,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
if (least->flags & IP_VS_DEST_F_OVERLOAD)
continue;
if ((atomic_read(&least->weight) > 0)
&& (least->flags & IP_VS_DEST_F_AVAILABLE)) {
if ((atomic_read(&least->weight) > 0) &&
(least->cflags & IP_VS_DEST_CF_AVAILABLE)) {
loh = ip_vs_dest_conn_overhead(least);
goto nextstage;
}
@@ -186,8 +186,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
doh = ip_vs_dest_conn_overhead(dest);
if (((__s64)loh * atomic_read(&dest->weight) >
(__s64)doh * atomic_read(&least->weight))
&& (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
(__s64)doh * atomic_read(&least->weight)) &&
(dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
least = dest;
loh = doh;
}
+2 -2
View File
@@ -30,7 +30,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
/*
* Simply select the server with the least number of
* (activeconns<<5) + inactconns
* (activeconns*256) + totalconns
* Except whose weight is equal to zero.
* If the weight is equal to zero, it means that the server is
* quiesced, the existing connections to the server still get
@@ -56,7 +56,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
atomic_read(&least->inactconns));
ip_vs_dest_inactconns(least));
return least;
}
-2
View File
@@ -446,12 +446,10 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
(next_state != IP_VS_SCTP_S_ESTABLISHED)) {
atomic_dec(&dest->activeconns);
atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
(next_state == IP_VS_SCTP_S_ESTABLISHED)) {
atomic_inc(&dest->activeconns);
atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
}
-2
View File
@@ -526,12 +526,10 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
!tcp_state_active(new_state)) {
atomic_dec(&dest->activeconns);
atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
tcp_state_active(new_state)) {
atomic_inc(&dest->activeconns);
atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
}
+2 -5
View File
@@ -878,13 +878,10 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa
spin_lock_bh(&cp->lock);
if ((cp->flags ^ flags) & IP_VS_CONN_F_INACTIVE &&
!(flags & IP_VS_CONN_F_TEMPLATE) && dest) {
if (flags & IP_VS_CONN_F_INACTIVE) {
if (flags & IP_VS_CONN_F_INACTIVE)
atomic_dec(&dest->activeconns);
atomic_inc(&dest->inactconns);
} else {
else
atomic_inc(&dest->activeconns);
atomic_dec(&dest->inactconns);
}
}
flags &= IP_VS_CONN_F_BACKUP_UPD_MASK;
flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK;
+4 -3
View File
@@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
dest->flags & IP_VS_DEST_F_AVAILABLE)
dest->cflags & IP_VS_DEST_CF_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
else
noref = 0;
@@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
dest->flags & IP_VS_DEST_F_AVAILABLE)
dest->cflags & IP_VS_DEST_CF_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
else
noref = 0;
@@ -1580,7 +1580,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph);
if (!ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph))
goto tx_error;
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
+6
View File
@@ -79,6 +79,12 @@ void nf_ct_l4proto_log_invalid(const struct sk_buff *skb,
struct net *net;
va_list args;
/* nfnetlink_log may re-enter conntrack attribute dumping and try to
* take ct->lock again via helpers such as tcp_to_nlattr(), so invalid
* conntrack logs must only be emitted after dropping ct->lock.
*/
lockdep_assert_not_held(&ct->lock);
net = nf_ct_net(ct);
if (likely(net->ct.sysctl_log_invalid == 0))
return;
+8 -4
View File
@@ -336,10 +336,12 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
struct sctphdr _sctph;
const struct sctp_chunkhdr *sch;
struct sctp_chunkhdr _sch;
bool log_invalid = false;
u_int32_t offset, count;
unsigned int *timeouts;
unsigned long map[256 / sizeof(unsigned long)] = { 0 };
bool ignore = false;
u8 invalid_type = 0;
if (sctp_error(skb, dataoff, state))
return -NF_ACCEPT;
@@ -451,10 +453,8 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
/* Invalid */
if (new_state == SCTP_CONNTRACK_MAX) {
nf_ct_l4proto_log_invalid(skb, ct, state,
"Invalid, old_state %d, dir %d, type %d",
old_state, dir, sch->type);
log_invalid = true;
invalid_type = sch->type;
goto out_unlock;
}
@@ -529,6 +529,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
out_unlock:
spin_unlock_bh(&ct->lock);
if (log_invalid)
nf_ct_l4proto_log_invalid(skb, ct, state,
"Invalid, old_state %d, dir %d, type %d",
old_state, dir, invalid_type);
out:
return -NF_ACCEPT;
}
+88 -44
View File
@@ -480,37 +480,81 @@ static void tcp_init_sender(struct ip_ct_tcp_state *sender,
}
}
__printf(6, 7)
static enum nf_ct_tcp_action nf_tcp_log_invalid(const struct sk_buff *skb,
const struct nf_conn *ct,
const struct nf_hook_state *state,
const struct ip_ct_tcp_state *sender,
enum nf_ct_tcp_action ret,
const char *fmt, ...)
enum nf_tcp_invalid_log_type {
NF_TCP_LOG_NONE,
NF_TCP_LOG_OVERSHOT,
NF_TCP_LOG_SEQ_OVER,
NF_TCP_LOG_ACK_OVER,
NF_TCP_LOG_SEQ_UNDER,
NF_TCP_LOG_ACK_UNDER,
};
struct nf_tcp_invalid_log {
enum nf_tcp_invalid_log_type type;
u32 value;
};
static enum nf_ct_tcp_action
nf_tcp_store_invalid(const struct nf_conn *ct,
const struct ip_ct_tcp_state *sender,
struct nf_tcp_invalid_log *log,
enum nf_ct_tcp_action ret,
enum nf_tcp_invalid_log_type type,
u32 value)
{
const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct));
struct va_format vaf;
va_list args;
bool be_liberal;
be_liberal = sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal;
if (be_liberal)
return NFCT_TCP_ACCEPT;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
nf_ct_l4proto_log_invalid(skb, ct, state, "%pV", &vaf);
va_end(args);
log->type = type;
log->value = value;
return ret;
}
static void nf_tcp_log_invalid(const struct sk_buff *skb,
const struct nf_conn *ct,
const struct nf_hook_state *state,
const struct nf_tcp_invalid_log *log)
{
switch (log->type) {
case NF_TCP_LOG_OVERSHOT:
nf_ct_l4proto_log_invalid(skb, ct, state,
"%u bytes more than expected",
log->value);
break;
case NF_TCP_LOG_SEQ_OVER:
nf_ct_l4proto_log_invalid(skb, ct, state,
"SEQ is over upper bound %u (over the window of the receiver)",
log->value);
break;
case NF_TCP_LOG_ACK_OVER:
nf_ct_l4proto_log_invalid(skb, ct, state,
"ACK is over upper bound %u (ACKed data not seen yet)",
log->value);
break;
case NF_TCP_LOG_SEQ_UNDER:
nf_ct_l4proto_log_invalid(skb, ct, state,
"SEQ is under lower bound %u (already ACKed data retransmitted)",
log->value);
break;
case NF_TCP_LOG_ACK_UNDER:
nf_ct_l4proto_log_invalid(skb, ct, state,
"ignored ACK under lower bound %u (possible overly delayed)",
log->value);
break;
case NF_TCP_LOG_NONE:
break;
}
}
static enum nf_ct_tcp_action
tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
unsigned int index, const struct sk_buff *skb,
unsigned int dataoff, const struct tcphdr *tcph,
const struct nf_hook_state *hook_state)
struct nf_tcp_invalid_log *log)
{
struct ip_ct_tcp *state = &ct->proto.tcp;
struct ip_ct_tcp_state *sender = &state->seen[dir];
@@ -640,31 +684,29 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
sender->td_end = end;
sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED;
return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
"%u bytes more than expected", overshot);
return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
NF_TCP_LOG_OVERSHOT, overshot);
}
return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID,
"SEQ is over upper bound %u (over the window of the receiver)",
sender->td_maxend + 1);
return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID,
NF_TCP_LOG_SEQ_OVER, sender->td_maxend + 1);
}
if (!before(sack, receiver->td_end + 1))
return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID,
"ACK is over upper bound %u (ACKed data not seen yet)",
receiver->td_end + 1);
return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID,
NF_TCP_LOG_ACK_OVER, receiver->td_end + 1);
/* Is the ending sequence in the receive window (if available)? */
in_recv_win = !receiver->td_maxwin ||
after(end, sender->td_end - receiver->td_maxwin - 1);
if (!in_recv_win)
return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
"SEQ is under lower bound %u (already ACKed data retransmitted)",
sender->td_end - receiver->td_maxwin - 1);
return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
NF_TCP_LOG_SEQ_UNDER,
sender->td_end - receiver->td_maxwin - 1);
if (!after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1))
return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
"ignored ACK under lower bound %u (possible overly delayed)",
receiver->td_end - MAXACKWINDOW(sender) - 1);
return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
NF_TCP_LOG_ACK_UNDER,
receiver->td_end - MAXACKWINDOW(sender) - 1);
/* Take into account window scaling (RFC 1323). */
if (!tcph->syn)
@@ -719,11 +761,8 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
return NFCT_TCP_ACCEPT;
}
static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
enum ip_conntrack_dir dir,
int index,
const struct sk_buff *skb,
const struct nf_hook_state *hook_state)
static bool __cold
nf_tcp_handle_invalid(struct nf_conn *ct, enum ip_conntrack_dir dir, int index)
{
const unsigned int *timeouts;
const struct nf_tcp_net *tn;
@@ -732,7 +771,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
if (!test_bit(IPS_ASSURED_BIT, &ct->status) ||
test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status))
return;
return false;
/* We don't want to have connections hanging around in ESTABLISHED
* state for long time 'just because' conntrack deemed a FIN/RST
@@ -747,7 +786,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
case TCP_FIN_SET:
break;
default:
return;
return false;
}
if (ct->proto.tcp.last_dir != dir &&
@@ -755,7 +794,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
ct->proto.tcp.last_index == TCP_RST_SET)) {
expires = nf_ct_expires(ct);
if (expires < 120 * HZ)
return;
return false;
tn = nf_tcp_pernet(nf_ct_net(ct));
timeouts = nf_ct_timeout_lookup(ct);
@@ -764,16 +803,15 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
timeout = READ_ONCE(timeouts[TCP_CONNTRACK_UNACK]);
if (expires > timeout) {
nf_ct_l4proto_log_invalid(skb, ct, hook_state,
"packet (index %d, dir %d) response for index %d lower timeout to %u",
index, dir, ct->proto.tcp.last_index, timeout);
WRITE_ONCE(ct->timeout, timeout + nfct_time_stamp);
return true;
}
} else {
ct->proto.tcp.last_index = index;
ct->proto.tcp.last_dir = dir;
}
return false;
}
/* table of valid flag combinations - PUSH, ECE and CWR are always valid */
@@ -969,7 +1007,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
struct net *net = nf_ct_net(ct);
struct nf_tcp_net *tn = nf_tcp_pernet(net);
enum tcp_conntrack new_state, old_state;
struct nf_tcp_invalid_log log = {};
unsigned int index, *timeouts;
bool lowered_timeout = false;
enum nf_ct_tcp_action res;
enum ip_conntrack_dir dir;
const struct tcphdr *th;
@@ -1252,14 +1292,18 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
}
res = tcp_in_window(ct, dir, index,
skb, dataoff, th, state);
skb, dataoff, th, &log);
switch (res) {
case NFCT_TCP_IGNORE:
spin_unlock_bh(&ct->lock);
nf_tcp_log_invalid(skb, ct, state, &log);
return NF_ACCEPT;
case NFCT_TCP_INVALID:
nf_tcp_handle_invalid(ct, dir, index, skb, state);
lowered_timeout = nf_tcp_handle_invalid(ct, dir, index);
spin_unlock_bh(&ct->lock);
nf_tcp_log_invalid(skb, ct, state, &log);
if (lowered_timeout)
nf_ct_l4proto_log_invalid(skb, ct, state, "lowered timeout to UNACK");
return -NF_ACCEPT;
case NFCT_TCP_ACCEPT:
break;
+4 -3
View File
@@ -332,17 +332,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[0].node,
&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node,
nf_flow_offload_rhash_params);
if (err < 0)
return err;
/* GC only iterates original-direction entries; publish original last. */
err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[1].node,
&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node,
nf_flow_offload_rhash_params);
if (err < 0) {
rhashtable_remove_fast(&flow_table->rhashtable,
&flow->tuplehash[0].node,
&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node,
nf_flow_offload_rhash_params);
return err;
}
+2 -2
View File
@@ -1014,7 +1014,7 @@ err1:
return ret;
}
static void __net_exit nf_log_syslog_net_exit(struct net *net)
static void __net_exit nf_log_syslog_net_pre_exit(struct net *net)
{
nf_log_unset(net, &nf_ip_logger);
nf_log_unset(net, &nf_arp_logger);
@@ -1025,7 +1025,7 @@ static void __net_exit nf_log_syslog_net_exit(struct net *net)
static struct pernet_operations nf_log_syslog_net_ops = {
.init = nf_log_syslog_net_init,
.exit = nf_log_syslog_net_exit,
.pre_exit = nf_log_syslog_net_pre_exit,
};
static int __init nf_log_syslog_init(void)
+1 -1
View File
@@ -558,7 +558,7 @@ static void nft_flow_rule_offload_abort(struct net *net,
break;
}
if (WARN_ON_ONCE(err))
if (WARN_ON_ONCE(err && err != -ENOMEM))
break;
}
}
+9 -4
View File
@@ -1170,21 +1170,26 @@ static int __net_init nfnl_log_net_init(struct net *net)
return 0;
}
static void __net_exit nfnl_log_net_pre_exit(struct net *net)
{
#ifdef CONFIG_PROC_FS
remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter);
#endif
nf_log_unset(net, &nfulnl_logger);
}
static void __net_exit nfnl_log_net_exit(struct net *net)
{
struct nfnl_log_net *log = nfnl_log_pernet(net);
unsigned int i;
#ifdef CONFIG_PROC_FS
remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter);
#endif
nf_log_unset(net, &nfulnl_logger);
for (i = 0; i < INSTANCE_BUCKETS; i++)
WARN_ON_ONCE(!hlist_empty(&log->instance_table[i]));
}
static struct pernet_operations nfnl_log_net_ops = {
.init = nfnl_log_net_init,
.pre_exit = nfnl_log_net_pre_exit,
.exit = nfnl_log_net_exit,
.id = &nfnl_log_net_id,
.size = sizeof(struct nfnl_log_net),
+5 -2
View File
@@ -1943,13 +1943,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
sock->type == SOCK_RAW)
skb->protocol = dev_parse_header_protocol(skb);
skb_probe_transport_header(skb);
/* Move network header to the right position for VLAN tagged packets */
if (likely(skb->dev->type == ARPHRD_ETHER) &&
eth_type_vlan(skb->protocol) &&
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
skb_probe_transport_header(skb);
}
/*
@@ -2660,6 +2660,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
len = ((to_write > len_max) ? len_max : to_write);
}
if (unlikely(!skb->len))
return -EINVAL;
packet_parse_headers(skb, sock);
return tp_len;
+5 -5
View File
@@ -41,11 +41,9 @@ int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb))
}
EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit);
static void tcf_action_goto_chain_exec(const struct tc_action *a,
static void tcf_action_goto_chain_exec(const struct tcf_chain *chain,
struct tcf_result *res)
{
const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
res->goto_tp = rcu_dereference_bh(chain->filter_chain);
}
@@ -1170,12 +1168,14 @@ repeat:
return TC_ACT_OK;
}
} else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
if (unlikely(!rcu_access_pointer(a->goto_chain))) {
struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
if (unlikely(!chain)) {
tcf_set_drop_reason(skb,
SKB_DROP_REASON_TC_CHAIN_NOTFOUND);
return TC_ACT_SHOT;
}
tcf_action_goto_chain_exec(a, res);
tcf_action_goto_chain_exec(chain, res);
}
if (ret != TC_ACT_PIPE)
+25 -4
View File
@@ -840,8 +840,15 @@ static int tcf_ct_ipv6_is_fragment(struct sk_buff *skb, bool *frag)
return 0;
}
/* On error, tells the caller whether it still owns @skb and must free it
* itself. @skb is ours only when the header checks below reject the packet
* before it is handed to the defragmentation engine; once nf_ct_handle_
* fragments() has been called the skb is either queued (-EINPROGRESS) or has
* already been freed by it.
*/
static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
u8 family, u16 zone, bool *defrag)
u8 family, u16 zone, bool *defrag,
bool *skb_is_ours)
{
enum ip_conntrack_info ctinfo;
struct tc_skb_cb cb;
@@ -859,8 +866,12 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
err = tcf_ct_ipv4_is_fragment(skb, &frag);
else
err = tcf_ct_ipv6_is_fragment(skb, &frag);
if (err || !frag)
if (err) {
*skb_is_ours = true;
return err;
}
if (!frag)
return 0;
cb = *tc_skb_cb(skb);
err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru);
@@ -977,6 +988,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
int nh_ofs, err, retval;
struct tcf_ct_params *p;
bool add_helper = false;
bool skb_is_ours = false;
bool skip_add = false;
bool defrag = false;
struct nf_conn *ct;
@@ -1012,9 +1024,18 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
*/
nh_ofs = skb_network_offset(skb);
skb_pull_rcsum(skb, nh_ofs);
err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag);
if (err)
err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag,
&skb_is_ours);
if (err) {
/* The skb is still ours only when the header checks rejected
* it; returning TC_ACT_CONSUMED for such a packet would leak
* it, since no caller frees an skb it was told it no longer
* owns.
*/
if (skb_is_ours)
goto drop;
goto out_frag;
}
err = nf_ct_skb_network_trim(skb, family);
if (err)
+5
View File
@@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
p_parm = nla_data(tb[TCA_GACT_PROB]);
if (p_parm->ptype >= MAX_RAND)
return -EINVAL;
if (!tcf_action_valid(p_parm->paction)) {
NL_SET_ERR_MSG(extack,
"invalid fallback control action");
return -EINVAL;
}
if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) {
NL_SET_ERR_MSG(extack,
"goto chain not allowed on fallback");
+6
View File
@@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
if (tb[TCA_POLICE_RESULT]) {
tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
if (!tcf_action_valid(tcfp_result)) {
NL_SET_ERR_MSG(extack,
"invalid fallback control action");
err = -EINVAL;
goto failure;
}
if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) {
NL_SET_ERR_MSG(extack,
"goto chain not allowed on fallback");
+16 -2
View File
@@ -374,7 +374,8 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog)
}
static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
u32 gen_flags, const struct tcf_proto *tp)
u32 gen_flags, const struct tcf_proto *tp,
struct netlink_ext_ack *extack)
{
struct bpf_prog *fp;
char *name = NULL;
@@ -388,6 +389,19 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
if (IS_ERR(fp))
return PTR_ERR(fp);
if (bpf_prog_is_dev_bound(fp->aux)) {
struct tcf_block *block = tp->chain->block;
struct net_device *dev;
dev = block->q ? qdisc_dev(block->q) : NULL;
if (!dev || !bpf_offload_dev_match(fp, dev)) {
NL_SET_ERR_MSG(extack,
"Program is bound to a different device");
bpf_prog_put(fp);
return -EINVAL;
}
}
if (tb[TCA_BPF_NAME]) {
name = nla_memdup(tb[TCA_BPF_NAME], GFP_KERNEL);
if (!name) {
@@ -492,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
prog->gen_flags = gen_flags;
ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) :
cls_bpf_prog_from_efd(tb, prog, gen_flags, tp);
cls_bpf_prog_from_efd(tb, prog, gen_flags, tp, extack);
if (ret < 0)
goto errout_idr;
+3
View File
@@ -1346,6 +1346,9 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
{
struct tc_u_knode *n = fh;
if (TC_U32_KEY(n->handle) == 0)
return;
tc_cls_bind_class(classid, cl, q, &n->res, base);
}
+6 -1
View File
@@ -543,6 +543,9 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
asoc->addip_last_asconf->transport == peer)
asoc->addip_last_asconf->transport = NULL;
if (asoc->new_transport == peer)
asoc->new_transport = NULL;
/* If we have something on the transmitted list, we have to
* save it off. The best place is the active path.
*/
@@ -1713,6 +1716,8 @@ void sctp_asconf_queue_teardown(struct sctp_association *asoc)
sctp_assoc_free_asconf_queue(asoc);
/* Free any cached ASCONF chunk. */
if (asoc->addip_last_asconf)
if (asoc->addip_last_asconf) {
sctp_chunk_free(asoc->addip_last_asconf);
asoc->addip_last_asconf = NULL;
}
}
+75
View File
@@ -377,6 +377,81 @@ nomem:
return -ENOMEM;
}
static bool sctp_auth_chunk_id_forbidden(__u8 chunk_id)
{
switch (chunk_id) {
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
case SCTP_CID_SHUTDOWN_COMPLETE:
case SCTP_CID_AUTH:
return true;
default:
return false;
}
}
/* Verify AUTH parameters copied from a state cookie before they are restored
* into an association. When cookie authentication is disabled these fields
* are peer-controlled, so they must satisfy the same constraints as locally
* generated AUTH parameters.
*/
bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep,
const struct sctp_cookie *cookie)
{
const struct sctp_paramhdr *random;
const struct sctp_hmac_algo_param *hmacs;
const struct sctp_chunks_param *chunks;
u16 hmacs_len, chunks_len;
u16 n_hmacs, n_chunks, i;
bool has_sha1 = false;
if (sctp_sk(ep->base.sk)->cookie_auth_enable || !ep->auth_enable)
return true;
random = (const struct sctp_paramhdr *)cookie->auth_random;
if (random->type != SCTP_PARAM_RANDOM ||
ntohs(random->length) != sizeof(*random) + SCTP_AUTH_RANDOM_LENGTH)
return false;
hmacs = (const struct sctp_hmac_algo_param *)cookie->auth_hmacs;
hmacs_len = ntohs(hmacs->param_hdr.length);
if (hmacs->param_hdr.type != SCTP_PARAM_HMAC_ALGO ||
hmacs_len < sizeof(struct sctp_paramhdr) +
sizeof(hmacs->hmac_ids[0]) ||
hmacs_len > sizeof(cookie->auth_hmacs) ||
(hmacs_len - sizeof(struct sctp_paramhdr)) %
sizeof(hmacs->hmac_ids[0]))
return false;
n_hmacs = (hmacs_len - sizeof(struct sctp_paramhdr)) /
sizeof(hmacs->hmac_ids[0]);
for (i = 0; i < n_hmacs; i++) {
u16 hmac_id = ntohs(hmacs->hmac_ids[i]);
if (!sctp_hmac_supported(hmac_id))
return false;
if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1)
has_sha1 = true;
}
if (!has_sha1)
return false;
chunks = (const struct sctp_chunks_param *)cookie->auth_chunks;
chunks_len = ntohs(chunks->param_hdr.length);
if (chunks->param_hdr.type != SCTP_PARAM_CHUNKS ||
chunks_len < sizeof(struct sctp_paramhdr) ||
chunks_len > sizeof(cookie->auth_chunks))
return false;
n_chunks = chunks_len - sizeof(struct sctp_paramhdr);
for (i = 0; i < n_chunks; i++) {
if (sctp_auth_chunk_id_forbidden(chunks->chunks[i]))
return false;
}
return true;
}
/* Public interface to create the association shared key.
* See code above for the algorithm.
+3
View File
@@ -1852,6 +1852,9 @@ struct sctp_association *sctp_unpack_cookie(
/* Set up our peer's port number. */
retval->peer.port = ntohs(chunk->sctp_hdr->source);
if (!sctp_auth_verify_cookie_params(ep, bear_cookie))
goto malformed;
/* Populate the association from the cookie. */
memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie));
+5 -1
View File
@@ -6145,8 +6145,12 @@ enum sctp_disposition sctp_sf_t4_timer_expire(
struct sctp_cmd_seq *commands)
{
struct sctp_chunk *chunk = asoc->addip_last_asconf;
struct sctp_transport *transport = chunk->transport;
struct sctp_transport *transport;
if (!chunk)
return SCTP_DISPOSITION_CONSUME;
transport = chunk->transport;
SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS);
/* ADDIP 4.1 B1) Increment the error counters and perform path failure
+10 -5
View File
@@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
{
struct tipc_link_entry *le = &n->links[bearer_id];
struct tipc_media_addr *maddr = NULL;
struct tipc_link *l = le->link;
int old_bearer_id = bearer_id;
struct tipc_link_entry *le;
struct sk_buff_head xmitq;
if (!l)
return;
struct tipc_link *l;
__skb_queue_head_init(&xmitq);
/* Synchronize the link lookup with bearer teardown. */
tipc_node_write_lock(n);
le = &n->links[bearer_id];
l = le->link;
if (!l) {
tipc_node_write_unlock_fast(n);
return;
}
if (!tipc_link_is_establishing(l)) {
__tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
} else {
+5
View File
@@ -2014,6 +2014,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
if (err < 0)
return err;
/* If crypto failed the connection is broken */
err = ctx->async_wait.err;
if (err)
goto splice_read_end;
if (!skb_queue_empty(&ctx->rx_list)) {
skb = __skb_dequeue(&ctx->rx_list);
} else {
+1
View File
@@ -186,6 +186,7 @@ static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
if (!vertex->out_degree) {
edge->predecessor->vertex = NULL;
list_move_tail(&vertex->entry, &fpl->vertices);
list_del(&vertex->scc_entry);
}
}
+65 -10
View File
@@ -24,6 +24,7 @@
#include "kselftest_harness.h"
#define TLS_PAYLOAD_MAX_LEN 16384
#define TLS_HDR_LEN 5
#define SOL_TLS 282
static int fips_enabled;
@@ -2732,28 +2733,83 @@ TEST_F(tls_err, bad_rec)
EXPECT_EQ(errno, EAGAIN);
}
/* cfd carries a byte stream, so one recv() can return part of a
* record. Take the fragment length from the record header and wait
* for the remainder.
*/
static void tls_send_bad_auth(struct __test_metadata *_metadata,
int fd, int cfd, int fd2)
{
char buf[128];
int len;
memrnd(buf, sizeof(buf) / 2);
ASSERT_EQ(send(fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2);
ASSERT_EQ(recv(cfd, buf, TLS_HDR_LEN, MSG_WAITALL), TLS_HDR_LEN);
len = ((unsigned char)buf[3] << 8) | (unsigned char)buf[4];
ASSERT_GT(len, 0);
ASSERT_LE(len, (int)sizeof(buf) - TLS_HDR_LEN);
ASSERT_EQ(recv(cfd, buf + TLS_HDR_LEN, len, MSG_WAITALL), len);
buf[TLS_HDR_LEN + len - 1]++;
ASSERT_EQ(send(fd2, buf, TLS_HDR_LEN + len, 0), TLS_HDR_LEN + len);
}
TEST_F(tls_err, bad_auth)
{
char buf[128];
int n;
if (self->notls)
SKIP(return, "no TLS support");
memrnd(buf, sizeof(buf) / 2);
EXPECT_EQ(send(self->fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2);
n = recv(self->cfd, buf, sizeof(buf), 0);
EXPECT_GT(n, sizeof(buf) / 2);
tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2);
buf[n - 1]++;
EXPECT_EQ(send(self->fd2, buf, n, 0), n);
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
EXPECT_EQ(errno, EBADMSG);
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
EXPECT_EQ(errno, EBADMSG);
}
/* A record that did not authenticate breaks the connection for every
* reader, splice included.
*
* The two decrypt paths reach that result differently. A synchronous
* decrypt leaves the record parsed, so the splice re-runs the decrypt
* and fails on the record itself; the ctx->async_wait.err check in
* tls_sw_splice_read() is not what stops it. Only an asynchronous
* decrypt, which needs a TLS 1.2 socket and an AEAD advertising
* CRYPTO_ALG_ASYNC, consumes the record before the failure is
* recorded, leaving that check the sole reason the splice fails.
*/
TEST_F(tls_err, bad_auth_splice)
{
char buf[128];
ssize_t ret;
int p[2];
if (self->notls)
SKIP(return, "no TLS support");
tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2);
EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1);
EXPECT_EQ(errno, EBADMSG);
ASSERT_GE(pipe(p), 0);
ret = splice(self->cfd2, NULL, p[1], NULL, sizeof(buf),
SPLICE_F_NONBLOCK);
EXPECT_EQ(ret, -1);
EXPECT_EQ(errno, EBADMSG);
close(p[0]);
close(p[1]);
}
TEST_F(tls_err, bad_in_large_read)
{
char txt[3][64];
@@ -3009,7 +3065,6 @@ static size_t parse_tls_records(struct __test_metadata *_metadata,
{
const __u8 *rec = rx_buf;
size_t total_plaintext_rx = 0;
const __u8 rec_header_len = 5;
while (rec < rx_buf + rx_len) {
__u16 record_payload_len;
@@ -3029,7 +3084,7 @@ static size_t parse_tls_records(struct __test_metadata *_metadata,
/* Plaintext must not exceed the specified limit */
ASSERT_LE(plaintext_len, max_payload_len);
rec += rec_header_len + record_payload_len;
rec += TLS_HDR_LEN + record_payload_len;
}
return total_plaintext_rx;
@@ -702,5 +702,45 @@
"$TC qdisc del dev $DUMMY clsact",
"$TC qdisc del dev $DUMMY root handle 1:"
]
},
{
"id": "c7a3",
"name": "Verify act_ct drops a packet whose header checks fail",
"category": [
"actions",
"ct",
"scapy"
],
"plugins": {
"requires": [
"nsPlugin",
"scapyPlugin"
]
},
"setup": [
[
"$TC qdisc del dev $DEV1 clsact",
0,
1,
2,
255
],
"$TC qdisc add dev $DEV1 clsact"
],
"cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol all prio 1 matchall action ct",
"scapy": [
{
"iface": "$DEV0",
"count": 10,
"packet": "Ether(type=0x86dd)/IPv6(nh=0, plen=0, src='::1', dst='::2')"
}
],
"expExitCode": "0",
"verifyCmd": "$TC -s qdisc show dev $DEV1 clsact",
"matchPattern": "dropped 10",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DEV1 clsact"
]
}
]