mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user