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

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

  Previous releases - regressions:

    - page_pool: keep frag_offset aligned for odd-sized requests

    - sched: fix u32 duplicate handle when node ID pool is exhausted

    - udp: create exceptions before socket matching

    - igmp: convert struct ip_sf_list to RCU

    - ip6_gre: check tunnel info before xmit in ip6gre_tunnel_xmit

    - rds: acquire the fastpath locks in rds_conn_shutdown()

    - tipc:
        - protect node reset trace dump with node lock
        - fix NULL deref in tipc_named_node_up() on empty publication
          list

    - bluetooth:
        - L2CAP: fix out-of-bounds write in l2cap_ecred_connect
        - hci_core: fix race condition during device registration

    - eth:
        - mlx5e: prevent stale XSK buffer release on refill retries
        - bridge: don't truncate the port group walk on teardown

  Previous releases - always broken:

    - gro: fix nesting of TCP GSO SKBs in skb_gro_receive_list()

    - sched: fix skb sizing and action leak on reoffload delete

    - tcp: fix use-after-free in do_tcp_getsockopt()

    - af_packet: don't cast tpacket_hdr.tp_len to int in
      tpacket_parse_header()

    - sctp: fix soft lockup from unpadded ASCONF-ACK parameter iteration

    - iptunnel: fix stale transport header during tunnel decapsulation

    - eth:
        - vxlan: fix use-after-free in vxlan_mdb_remote_src_del()
        - bonding: fix uninitialized transport header access in
          alb_determine_nd()"

* tag 'net-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (83 commits)
  net: gro: Fix nesting of TCP GSO SKBs in skb_gro_receive_list()
  net: stmmac: reconfigure RX packet parser table in stmmac_hw_setup() after reset
  net: airoha: enable RX_DONE interrupt for RX queue 31
  net/rds: don't let rds_conn_shutdown() consume a concurrent drop
  net/rds: acquire the fastpath locks in rds_conn_shutdown()
  net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks()
  net/rds: tcp: don't force RDS_CONN_RESETTING over a concurrent shutdown
  net/rds: clear cp_flags bits individually in rds_conn_path_reset()
  net/rds: use clear_bit_unlock() in release_refill()
  net/rds: use wq_has_sleeper() in release_in_xmit()
  net: usb: qmi_wwan: add Compal EXM-G1x support
  net: macb: exclude software FCS from TX byte statistics
  net: Remove conflicting altnames for dying netns in __dev_change_net_namespace().
  net: bridge: mcast: don't truncate the port group walk on teardown
  bonding: do not clear curr_active_slave prematurely when releasing all slaves
  net: qrtr: Send HELLO message on endpoint register
  octeontx2-af: Fix limiting SRIOV VF count logic
  bonding: alb: fix uninitialized transport header access in alb_determine_nd()
  s390/ctcm: Prevent XID null dereference
  net: psp: do not inherit the Rx association on clone
  ...
This commit is contained in:
Linus Torvalds
2026-09-03 10:18:12 -07:00
86 changed files with 1841 additions and 705 deletions

View File

@@ -360,6 +360,17 @@ attribute-sets:
name: tsoff
type: u32
byte-order: big-endian
-
name: filter-attrs
attributes:
-
name: orig-flags
type: u32
doc: bitmask of tuple fields to filter on, original direction
-
name: reply-flags
type: u32
doc: bitmask of tuple fields to filter on, reply direction
-
name: conntrack-attrs
attributes:
@@ -466,7 +477,7 @@ attribute-sets:
-
name: filter
type: nest
nested-attributes: tuple-attrs
nested-attributes: filter-attrs
-
name: status-mask
type: u32
@@ -591,10 +602,14 @@ operations:
request:
value: 0x101
attributes:
- mark
- filter
- tuple-orig
- tuple-reply
- status
- mark
- zone
- mark-mask
- filter
- status-mask
reply:
value: 0x100
attributes:

View File

@@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
}
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
static u8 btintel_version_tlv_min_len(u8 type)
{
switch (type) {
case INTEL_TLV_CNVI_TOP:
case INTEL_TLV_CNVR_TOP:
case INTEL_TLV_CNVI_BT:
case INTEL_TLV_CNVR_BT:
case INTEL_TLV_BUILD_NUM:
case INTEL_TLV_GIT_SHA1:
return sizeof(u32);
case INTEL_TLV_DEV_REV_ID:
case INTEL_TLV_TIME_STAMP:
return sizeof(u16);
case INTEL_TLV_IMAGE_TYPE:
case INTEL_TLV_BUILD_TYPE:
case INTEL_TLV_SECURE_BOOT:
case INTEL_TLV_OTP_LOCK:
case INTEL_TLV_API_LOCK:
case INTEL_TLV_DEBUG_LOCK:
case INTEL_TLV_LIMITED_CCE:
case INTEL_TLV_SBE_TYPE:
return sizeof(u8);
case INTEL_TLV_MIN_FW:
return 3;
case INTEL_TLV_OTP_BDADDR:
return sizeof(bdaddr_t);
default:
return 0;
}
}
int btintel_parse_version_tlv(struct hci_dev *hdev,
struct intel_version_tlv *version,
struct sk_buff *skb)
{
/* Consume Command Complete Status field */
skb_pull(skb, 1);
if (!skb_pull(skb, 1))
return -EINVAL;
/* Event parameters contain multiple TLVs. Read each of them
* and only keep the required data. Also, it use existing legacy
@@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
if (skb->len < tlv->len + sizeof(*tlv))
return -EINVAL;
if (tlv->len < btintel_version_tlv_min_len(tlv->type))
return -EINVAL;
switch (tlv->type) {
case INTEL_TLV_CNVI_TOP:
version->cnvi_top = get_unaligned_le32(tlv->val);
@@ -667,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
break;
case INTEL_TLV_FW_ID:
snprintf(version->fw_id, sizeof(version->fw_id),
"%s", tlv->val);
"%.*s", tlv->len, tlv->val);
break;
default:
/* Ignore rest of information */
@@ -686,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
{
struct sk_buff *skb;
const u8 param[1] = { 0xFF };
int err;
if (!version)
return -EINVAL;
@@ -704,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
return -EIO;
}
btintel_parse_version_tlv(hdev, version, skb);
err = btintel_parse_version_tlv(hdev, version, skb);
kfree_skb(skb);
return 0;
return err;
}
/* ------- REGMAP IBT SUPPORT ------- */

View File

@@ -1696,6 +1696,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (unlikely(!(intr_fh | intr_hw))) {
/* Ignore interrupt, inta == 0 */
bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n");
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
BIT(entry->entry));
return IRQ_NONE;
}

View File

@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(2000));
if (err == 1) {
if (err == -EINTR) {
bt_dev_err(hdev, "Firmware load interrupted");
err = -EINTR;
break;
} else if (err) {
bt_dev_err(hdev, "Firmware request timeout");

View File

@@ -875,7 +875,7 @@ static int rlb_initialize(struct bonding *bond)
spin_unlock_bh(&bond->mode_lock);
/* register to receive ARPs */
bond->recv_probe = rlb_arp_recv;
WRITE_ONCE(bond->recv_probe, rlb_arp_recv);
return 0;
}
@@ -1281,10 +1281,10 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
}
/* determine if the packet is NA or NS */
static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
static bool alb_determine_nd(struct sk_buff *skb)
{
struct ipv6hdr *ip6hdr;
struct icmp6hdr *hdr;
const struct ipv6hdr *ip6hdr;
const struct icmp6hdr *hdr;
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr)))
return true;
@@ -1296,7 +1296,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr)))
return true;
hdr = icmp6_hdr(skb);
ip6hdr = ipv6_hdr(skb);
hdr = (const struct icmp6hdr *)(ip6hdr + 1);
return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT ||
hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION;
}
@@ -1381,7 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,
if (!is_multicast_ether_addr(eth_data->h_dest)) {
switch (skb->protocol) {
case htons(ETH_P_IPV6):
if (alb_determine_nd(skb, bond))
if (alb_determine_nd(skb))
break;
fallthrough;
case htons(ETH_P_IP):
@@ -1467,7 +1468,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,
break;
}
if (alb_determine_nd(skb, bond)) {
if (alb_determine_nd(skb)) {
do_tx_balance = false;
break;
}

View File

@@ -1245,7 +1245,7 @@ static void bond_peer_notify_may_events(struct bonding *bond, bool force)
}
if (notified || force)
bond->send_peer_notif--;
WRITE_ONCE(bond->send_peer_notif, bond->send_peer_notif - 1);
}
/**
@@ -2284,7 +2284,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
}
}
bond->slave_cnt++;
WRITE_ONCE(bond->slave_cnt, bond->slave_cnt + 1);
netdev_compute_master_upper_features(bond->dev, true);
bond_set_carrier(bond);
@@ -2517,9 +2517,7 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_alb_deinit_slave(bond, slave);
}
if (all) {
RCU_INIT_POINTER(bond->curr_active_slave, NULL);
} else if (oldcurrent == slave) {
if (!all && oldcurrent == slave) {
/* Note that we hold RTNL over this sequence, so there
* is no concern that another slave add/remove event
* will interfere.
@@ -2533,7 +2531,7 @@ static int __bond_release_one(struct net_device *bond_dev,
unblock_netpoll_tx();
synchronize_rcu();
bond->slave_cnt--;
WRITE_ONCE(bond->slave_cnt, bond->slave_cnt - 1);
if (!bond_has_slaves(bond)) {
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
@@ -4385,13 +4383,13 @@ static int bond_open(struct net_device *bond_dev)
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
queue_delayed_work(bond->wq, &bond->arp_work, 0);
bond->recv_probe = bond_rcv_validate;
WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
}
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
bond->recv_probe = bond_3ad_lacpdu_recv;
WRITE_ONCE(bond->recv_probe, bond_3ad_lacpdu_recv);
bond_3ad_initiate_agg_selection(bond, 1);
bond_for_each_slave(bond, slave, iter)
@@ -4413,7 +4411,7 @@ static int bond_close(struct net_device *bond_dev)
struct slave *slave;
bond_work_cancel_all(bond);
bond->send_peer_notif = 0;
WRITE_ONCE(bond->send_peer_notif, 0);
WRITE_ONCE(bond->recv_probe, NULL);
/* Wait for any in-flight RX handlers */
@@ -5118,7 +5116,7 @@ static void bond_skip_slave(struct bond_up_slave *slaves,
if (skipslave == slaves->arr[idx]) {
slaves->arr[idx] =
slaves->arr[slaves->count - 1];
slaves->count--;
WRITE_ONCE(slaves->count, slaves->count - 1);
break;
}
}

View File

@@ -1147,11 +1147,11 @@ static int bond_option_arp_interval_set(struct bonding *bond,
*/
if (!newval->value) {
if (bond->params.arp_validate)
bond->recv_probe = NULL;
WRITE_ONCE(bond->recv_probe, NULL);
cancel_delayed_work_sync(&bond->arp_work);
} else {
/* arp_validate can be set only in active-backup mode */
bond->recv_probe = bond_rcv_validate;
WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}

View File

@@ -538,7 +538,7 @@ struct airoha_wdma_info {
/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */
#define RX_IRQ0_BANK_PIN_MASK 0x839f
#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000
#define RX_IRQ1_BANK_PIN_MASK 0xffe00000
#define RX_IRQ2_BANK_PIN_MASK 0x20
#define RX_IRQ3_BANK_PIN_MASK 0x40
#define RX_IRQ_BANK_PIN_MASK(_n) \

View File

@@ -3400,7 +3400,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata)
XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val);
/* Enable MAC Rx */
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1);
@@ -3411,7 +3411,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata)
unsigned int i;
/* Disable MAC Rx */
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0);

View File

@@ -968,6 +968,8 @@ struct macb_dma_desc_ptp {
* of the frame
* @mapping: DMA address of the skb's fragment buffer
* @size: size of the DMA mapped buffer
* @fcs_len: FCS bytes appended in software, 0 or ETH_FCS_LEN, only
* set for the last buffer of the frame
* @mapped_as_page: true when buffer was mapped with skb_frag_dma_map(),
* false when buffer was mapped with dma_map_single()
*/
@@ -975,6 +977,7 @@ struct macb_tx_skb {
struct sk_buff *skb;
dma_addr_t mapping;
size_t size;
u8 fcs_len;
bool mapped_as_page;
};

View File

@@ -1322,8 +1322,8 @@ static void macb_tx_error_task(struct work_struct *work)
bp->netdev->stats.tx_packets++;
queue->stats.tx_packets++;
packets++;
bp->netdev->stats.tx_bytes += skb->len;
queue->stats.tx_bytes += skb->len;
bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
bytes += skb->len;
}
} else {
@@ -1450,8 +1450,8 @@ static int macb_tx_complete(struct macb_queue *queue, int budget)
skb->data);
bp->netdev->stats.tx_packets++;
queue->stats.tx_packets++;
bp->netdev->stats.tx_bytes += skb->len;
queue->stats.tx_bytes += skb->len;
bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
packets++;
bytes += skb->len;
}
@@ -2199,7 +2199,8 @@ static void macb_poll_controller(struct net_device *netdev)
static unsigned int macb_tx_map(struct macb *bp,
struct macb_queue *queue,
struct sk_buff *skb,
unsigned int hdrlen)
unsigned int hdrlen,
u8 fcs_len)
{
unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
unsigned int len, i, tx_head = queue->tx_head;
@@ -2284,6 +2285,7 @@ static unsigned int macb_tx_map(struct macb *bp,
/* This is the last buffer of the frame: save socket buffer */
tx_skb->skb = skb;
tx_skb->fcs_len = fcs_len;
/* Update TX ring: update buffer descriptors in reverse order
* to avoid race condition
@@ -2417,6 +2419,7 @@ static inline int macb_clear_csum(struct sk_buff *skb)
return 0;
}
/* Returns a negative errno, or the FCS bytes appended (0 or ETH_FCS_LEN). */
static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)
{
bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) ||
@@ -2465,7 +2468,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)
skb_put_u8(*skb, (fcs >> 16) & 0xff);
skb_put_u8(*skb, (fcs >> 24) & 0xff);
return 0;
return ETH_FCS_LEN;
}
static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
@@ -2478,6 +2481,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
netdev_tx_t ret = NETDEV_TX_OK;
unsigned int hdrlen;
unsigned long flags;
int fcs_len;
bool is_lso;
if (macb_clear_csum(skb)) {
@@ -2485,7 +2489,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
return ret;
}
if (macb_pad_and_fcs(&skb, netdev)) {
fcs_len = macb_pad_and_fcs(&skb, netdev);
if (fcs_len < 0) {
dev_kfree_skb_any(skb);
return ret;
}
@@ -2548,7 +2553,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
}
/* Map socket buffer for DMA transfer */
if (macb_tx_map(bp, queue, skb, hdrlen)) {
if (macb_tx_map(bp, queue, skb, hdrlen, fcs_len)) {
dev_kfree_skb_any(skb);
goto unlock;
}

View File

@@ -3468,6 +3468,8 @@ int rvu_get_num_lbk_chans(void)
return ret;
}
#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8
static int rvu_enable_sriov(struct rvu *rvu)
{
struct pci_dev *pdev = rvu->pdev;
@@ -3486,24 +3488,27 @@ static int rvu_enable_sriov(struct rvu *rvu)
return 0;
pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &rvu->vf_devid);
chans = rvu_get_num_lbk_chans();
if (chans < 0)
return chans;
vfs = pci_sriov_get_totalvfs(pdev);
/* Limit VFs in case we have more VFs than LBK channels available. */
if (vfs > chans)
vfs = chans;
if (!vfs)
return 0;
/* LBK channel number 63 is used for switching packets between
* CGX mapped VFs. Hence limit LBK pairs till 62 only.
*/
if (vfs > 62)
vfs = 62;
if (rvu->vf_devid == PCI_DEVID_OCTEONTX2_RVU_AFVF) {
chans = rvu_get_num_lbk_chans();
if (chans < 0)
return chans;
/* The last LBK channel is reserved for switching packets between
* CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF
* count to available LBK channels minus 2.
*/
vfs = min(vfs, chans - 2);
if (vfs <= 0) {
dev_warn(&pdev->dev,
"Skipping SRIOV enablement, not enough LBK channels available\n");
return 0;
}
}
/* Save VFs number for reference in VF interrupts handlers.
* Since interrupts might start arriving during SRIOV enablement

View File

@@ -2957,10 +2957,9 @@ static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
}
}
/* Disable the entry */
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
if (!cn20k_dft_rl) {
/* Disable the entry */
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
/* Free the entry in bitmap */
npc_mcam_clear_bit(mcam, index);

View File

@@ -3,6 +3,7 @@
#include "rx.h"
#include "en/xdp.h"
#include <linux/bitmap.h>
#include <net/xdp_sock_drv.h>
#include <linux/filter.h>
@@ -156,6 +157,7 @@ int mlx5e_xsk_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
xsk_buff_free(xsk_buffs[batch]);
err:
bitmap_fill(wi->skip_release_bitmap, rq->mpwqe.pages_per_wqe);
rq->stats->buff_alloc_err++;
return -ENOMEM;
}

View File

@@ -410,8 +410,11 @@ static inline void mlx5e_free_rx_wqe(struct mlx5e_rq *rq,
static void mlx5e_xsk_free_rx_wqe(struct mlx5e_wqe_frag_info *wi)
{
if (!(wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE)))
xsk_buff_free(*wi->xskp);
if (wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE))
return;
xsk_buff_free(*wi->xskp);
wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);
}
static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)

View File

@@ -83,7 +83,7 @@ struct oa_tc6 {
u16 spi_data_tx_buf_offset;
u16 tx_credits;
u8 rx_chunks_available;
bool rx_buf_overflow;
bool wait_until_start_valid;
bool int_flag;
bool disable_traffic;
bool prot_ctrl;
@@ -455,7 +455,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
{
struct oa_tc6 *tc6 = bus->priv;
u32 regval;
bool ret;
int ret;
ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE |
(regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
@@ -693,6 +693,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)
return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value);
}
/* Called when a frame that is meant to be transmitted, is dropped. */
static void oa_tc6_drop_tx_skb(struct oa_tc6 *tc6, struct sk_buff *skb)
{
if (skb) {
tc6->netdev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
}
}
static struct sk_buff *oa_tc6_detach_waiting_tx_skb(struct oa_tc6 *tc6)
{
struct sk_buff *skb;
lockdep_assert_held(&tc6->tx_skb_lock);
skb = tc6->waiting_tx_skb;
tc6->waiting_tx_skb = NULL;
return skb;
}
static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
{
if (tc6->rx_skb) {
@@ -704,29 +724,39 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6)
{
if (tc6->ongoing_tx_skb) {
tc6->netdev->stats.tx_dropped++;
kfree_skb(tc6->ongoing_tx_skb);
tc6->ongoing_tx_skb = NULL;
}
oa_tc6_drop_tx_skb(tc6, tc6->ongoing_tx_skb);
tc6->ongoing_tx_skb = NULL;
}
static void oa_tc6_cleanup_waiting_tx_skb(struct oa_tc6 *tc6)
{
if (tc6->waiting_tx_skb) {
tc6->netdev->stats.tx_dropped++;
kfree_skb(tc6->waiting_tx_skb);
tc6->waiting_tx_skb = NULL;
}
struct sk_buff *skb;
spin_lock_bh(&tc6->tx_skb_lock);
skb = oa_tc6_detach_waiting_tx_skb(tc6);
spin_unlock_bh(&tc6->tx_skb_lock);
oa_tc6_drop_tx_skb(tc6, skb);
}
static void oa_tc6_free_ongoing_skbs(struct oa_tc6 *tc6)
{
oa_tc6_cleanup_ongoing_tx_skb(tc6);
oa_tc6_cleanup_ongoing_rx_skb(tc6);
}
static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
{
oa_tc6_cleanup_ongoing_tx_skb(tc6);
oa_tc6_cleanup_ongoing_rx_skb(tc6);
oa_tc6_free_ongoing_skbs(tc6);
oa_tc6_cleanup_waiting_tx_skb(tc6);
}
static void oa_tc6_look_for_new_frame(struct oa_tc6 *tc6)
{
tc6->wait_until_start_valid = true;
oa_tc6_cleanup_ongoing_rx_skb(tc6);
}
/* If the failure is at SPI interface level, masking and clearing
* the interrupt of the device won't work. Since SPI interrupt is
* disabled, it should stop the repeated interrupts.
@@ -734,9 +764,19 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
static void oa_tc6_disable_traffic(struct oa_tc6 *tc6)
{
u32 regval = OA_TC6_INT_MASK0_ALL_INTERRUPTS;
struct sk_buff *skb;
spin_lock_bh(&tc6->tx_skb_lock);
tc6->disable_traffic = true;
oa_tc6_free_pending_skbs(tc6);
skb = oa_tc6_detach_waiting_tx_skb(tc6);
spin_unlock_bh(&tc6->tx_skb_lock);
/* disable_traffic, when set, is a point of no return to
* working state. Keeping the TX queues disabled.
*/
netif_tx_disable(tc6->netdev);
oa_tc6_drop_tx_skb(tc6, skb);
oa_tc6_free_ongoing_skbs(tc6);
oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval);
oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &regval);
oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval);
@@ -755,6 +795,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
return ret;
}
/* This function is called for each chunk received in a given SPI
* transaction. In case, extended status bit is set in more than
* one chunk, skip the write, if status0 is already cleared.
*/
if (!value)
return 0;
/* Clear the error interrupts status */
ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value);
if (ret) {
@@ -764,8 +811,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
}
if (FIELD_GET(OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
tc6->rx_buf_overflow = true;
oa_tc6_cleanup_ongoing_rx_skb(tc6);
oa_tc6_look_for_new_frame(tc6);
net_err_ratelimited("%s: Receive buffer overflow error\n",
tc6->netdev->name);
return -EAGAIN;
@@ -791,6 +837,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
{
int ret = 0;
/* Process rx chunk footer for the following,
* 1. tx credits
* 2. errors if any from MAC-PHY
@@ -801,9 +849,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
footer);
if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) {
int ret = oa_tc6_process_extended_status(tc6);
if (ret)
ret = oa_tc6_process_extended_status(tc6);
/* EAGAIN error is recoverable. Move on to check
* HEADER and SYNC errors before returning.
*/
if (ret && ret != -EAGAIN)
return ret;
}
@@ -821,7 +871,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
return -ENODEV;
}
return 0;
return ret;
}
static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
@@ -846,13 +896,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
tc6->rx_skb = NULL;
}
static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
/* On oversubscribed traffic condition, particularly with overwhelming rx
* buffer overflow errors, there could be data chunk loss. If tail + length
* goes beyond end pointer, that is an indication that the data chunk with
* end_valid bit is lost. Time to look for a data chunk with start_valid bit.
*
* If rx_skb is NULL, it is time to start looking for data chunk with
* start_bit.
*/
static int oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
{
if (!tc6->rx_skb ||
skb_tailroom(tc6->rx_skb) < length) {
oa_tc6_look_for_new_frame(tc6);
return -EAGAIN;
}
memcpy(skb_put(tc6->rx_skb, length), payload, length);
return 0;
}
/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is
* possible that we get two data chunks with start_valid bit set, without
* end_valid bit set in between. In this case, rx_skb would have a valid
* buffer pointer. We should release, if a valid pointer is found before
* allocating a new one.
*/
static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6)
{
oa_tc6_cleanup_ongoing_rx_skb(tc6);
tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu +
ETH_HLEN + ETH_FCS_LEN);
if (!tc6->rx_skb) {
@@ -872,7 +944,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,
if (ret)
return ret;
oa_tc6_update_rx_skb(tc6, payload, size);
ret = oa_tc6_update_rx_skb(tc6, payload, size);
if (ret)
return ret;
oa_tc6_submit_rx_skb(tc6);
@@ -887,22 +961,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)
if (ret)
return ret;
oa_tc6_update_rx_skb(tc6, payload, size);
return 0;
return oa_tc6_update_rx_skb(tc6, payload, size);
}
static void oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
static int oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
{
oa_tc6_update_rx_skb(tc6, payload, size);
int ret;
oa_tc6_submit_rx_skb(tc6);
ret = oa_tc6_update_rx_skb(tc6, payload, size);
if (!ret)
oa_tc6_submit_rx_skb(tc6);
return ret;
}
static void oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
u32 footer)
static int oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
u32 footer)
{
oa_tc6_update_rx_skb(tc6, payload, OA_TC6_CHUNK_PAYLOAD_SIZE);
return oa_tc6_update_rx_skb(tc6, payload,
OA_TC6_CHUNK_PAYLOAD_SIZE);
}
static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
@@ -917,10 +993,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
u16 size;
/* Restart the new rx frame after receiving rx buffer overflow error */
if (start_valid && tc6->rx_buf_overflow)
tc6->rx_buf_overflow = false;
if (start_valid && tc6->wait_until_start_valid)
tc6->wait_until_start_valid = false;
if (tc6->rx_buf_overflow)
if (tc6->wait_until_start_valid)
return 0;
/* Process the chunk with complete rx frame */
@@ -942,8 +1018,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
/* Process the chunk with only rx frame end */
if (end_valid && !start_valid) {
size = end_byte_offset + 1;
oa_tc6_prcs_rx_frame_end(tc6, data, size);
return 0;
return oa_tc6_prcs_rx_frame_end(tc6, data, size);
}
/* Process the chunk with previous rx frame end and next rx frame
@@ -957,6 +1032,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
if (tc6->rx_skb) {
size = end_byte_offset + 1;
oa_tc6_prcs_rx_frame_end(tc6, data, size);
/* Return value from oa_tc6_prcs_rx_frame_end is not
* checked. If it returned an error, it is to make
* the code to look for new frame. At this stage,
* code below is going to process a new frame. So,
* error condition is set to false, in case it is
* set before proceeding.
*/
tc6->wait_until_start_valid = false;
}
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset;
return oa_tc6_prcs_rx_frame_start(tc6,
@@ -965,9 +1049,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
}
/* Process the chunk with ongoing rx frame data */
oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
return 0;
return oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
}
static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
@@ -983,8 +1065,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
{
u16 no_of_rx_chunks = length / OA_TC6_CHUNK_SIZE;
bool retry = false;
int ret = 0;
u32 footer;
int ret;
/* All the rx chunks in the receive SPI data buffer are examined here */
for (int i = 0; i < no_of_rx_chunks; i++) {
@@ -993,8 +1076,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
OA_TC6_CHUNK_PAYLOAD_SIZE);
ret = oa_tc6_process_rx_chunk_footer(tc6, footer);
if (ret)
return ret;
if (ret) {
if (ret != -EAGAIN)
return ret;
retry = true;
}
/* If there is a data valid chunks then process it for the
* information needed to determine the validity and the location
@@ -1006,12 +1092,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload,
footer);
if (ret)
return ret;
if (ret) {
if (ret != -ENOMEM && ret != -EAGAIN)
return ret;
retry = true;
}
}
}
return 0;
/* Not bailing out on recoverable error codes, -EAGAIN and
* -ENOMEM. If subsequent loop iterations, if any, succeeds,
* error code would be overwritten. retry flag helps to
* make the caller to continue and retry. Since recovery
* action for -ENOMEM and -EAGAIN are same, we are returning
* one of the error codes, that is -EAGAIN.
*
* Successful recovery depends on how small the frames are,
* how many chunks, among the received chunks triggered the
* error, whether data is intact even with error conditions.
* As a result, there is no single, best method to recover
* most data when error conditions hit. We do our best by
* processing all the chunks with good "footer header" and
* "data valid" bit set.
*/
if (retry) {
ret = -EAGAIN;
oa_tc6_look_for_new_frame(tc6);
}
return ret;
}
static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
@@ -1173,12 +1282,9 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)
}
ret = oa_tc6_process_spi_data_rx_buf(tc6, spi_len);
if (ret) {
if (ret == -EAGAIN)
continue;
oa_tc6_cleanup_ongoing_tx_skb(tc6);
oa_tc6_cleanup_ongoing_rx_skb(tc6);
if (ret && ret != -EAGAIN) {
oa_tc6_free_ongoing_skbs(tc6);
netdev_err(tc6->netdev, "Device error: %d\n", ret);
return ret;
}
@@ -1200,15 +1306,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)
* no need to attempt spi transfer, once it fails. Pending skbs
* are already freed.
*/
if (!tc6->disable_traffic) {
while (tc6->int_flag ||
(tc6->waiting_tx_skb && tc6->tx_credits)) {
ret = oa_tc6_try_spi_transfer(tc6);
if (ret) {
disable_irq_nosync(tc6->spi->irq);
oa_tc6_disable_traffic(tc6);
break;
}
spin_lock_bh(&tc6->tx_skb_lock);
if (tc6->disable_traffic) {
spin_unlock_bh(&tc6->tx_skb_lock);
return IRQ_HANDLED;
}
spin_unlock_bh(&tc6->tx_skb_lock);
while (tc6->int_flag ||
(tc6->waiting_tx_skb && tc6->tx_credits)) {
ret = oa_tc6_try_spi_transfer(tc6);
if (ret) {
disable_irq_nosync(tc6->spi->irq);
oa_tc6_disable_traffic(tc6);
break;
}
}
@@ -1287,23 +1398,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);
* @tc6: oa_tc6 struct.
* @skb: socket buffer in which the ethernet frame is stored.
*
* Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q
* otherwise returns NETDEV_TX_BUSY.
* Return: NETDEV_TX_OK either on successful queueing of the packet for
* transmission, or on packet getting dropped. Packet can be dropped due to
* failure in linearizing the buffer or disable_traffic is set due to
* earlier fatal error. Returns NETDEV_TX_BUSY when there is no room
* to queue the packet.
*/
netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb)
{
if (tc6->disable_traffic || tc6->waiting_tx_skb) {
netif_stop_queue(tc6->netdev);
return NETDEV_TX_BUSY;
}
if (skb_linearize(skb)) {
dev_kfree_skb_any(skb);
tc6->netdev->stats.tx_dropped++;
oa_tc6_drop_tx_skb(tc6, skb);
return NETDEV_TX_OK;
}
spin_lock_bh(&tc6->tx_skb_lock);
if (tc6->waiting_tx_skb) {
netif_stop_queue(tc6->netdev);
spin_unlock_bh(&tc6->tx_skb_lock);
return NETDEV_TX_BUSY;
}
if (tc6->disable_traffic) {
spin_unlock_bh(&tc6->tx_skb_lock);
oa_tc6_drop_tx_skb(tc6, skb);
return NETDEV_TX_OK;
}
tc6->waiting_tx_skb = skb;
spin_unlock_bh(&tc6->tx_skb_lock);
@@ -1462,8 +1580,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);
*/
void oa_tc6_exit(struct oa_tc6 *tc6)
{
tc6->disable_traffic = true;
disable_irq(tc6->spi->irq);
spin_lock_bh(&tc6->tx_skb_lock);
tc6->disable_traffic = true;
spin_unlock_bh(&tc6->tx_skb_lock);
oa_tc6_phy_exit(tc6);
oa_tc6_free_pending_skbs(tc6);
}

View File

@@ -3676,6 +3676,14 @@ static int stmmac_hw_setup(struct net_device *dev)
/* Initialize MTL*/
stmmac_mtl_configuration(priv);
/* Apply the RX packet parser table */
if (priv->tc_entries) {
ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
priv->tc_entries_max);
if (ret)
return ret;
}
/* Initialize Safety Features */
stmmac_safety_feat_configuration(priv);
@@ -4319,6 +4327,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
/**
* stmmac_tso_allocator - close entry point of the driver
* @priv: driver private structure
* @entry: TX queue buffer index
* @des: buffer start address
* @total_len: total length to fill in descriptors
* @last_segment: condition for the last descriptor
@@ -4327,8 +4336,9 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
* This function fills descriptor and request new descriptors according to
* buffer length to fill
*/
static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
int total_len, bool last_segment, u32 queue)
static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
dma_addr_t des, int total_len,
bool last_segment, u32 queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
struct dma_desc *desc;
@@ -4340,14 +4350,13 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
while (tmp_len > 0) {
dma_addr_t curr_addr;
tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
priv->dma_conf.dma_tx_size);
WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
*entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
WARN_ON(tx_q->tx_skbuff[*entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
desc = &tx_q->dma_entx[*entry].basic;
else
desc = &tx_q->dma_tx[tx_q->cur_tx];
desc = &tx_q->dma_tx[*entry];
curr_addr = des + (total_len - tmp_len);
stmmac_set_desc_addr(priv, desc, curr_addr);
@@ -4486,7 +4495,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int first_entry, tx_packets;
unsigned int first_entry, entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
bool set_ic, is_last_segment;
@@ -4549,22 +4558,24 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
WARN_ON(tx_q->tx_skbuff[first_entry]);
entry = first_entry;
WARN_ON(tx_q->tx_skbuff[entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
desc = &tx_q->dma_entx[first_entry].basic;
desc = &tx_q->dma_entx[entry].basic;
else
desc = &tx_q->dma_tx[first_entry];
desc = &tx_q->dma_tx[entry];
first = desc;
/* first descriptor: fill Headers on Buf1 */
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
goto error;
stmmac_set_desc_addr(priv, first, des);
stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
stmmac_tso_allocator(priv, &entry, des + proto_hdr_len, pay_len,
(nfrags == 0), queue);
/* In case two or more DMA transmit descriptors are allocated for this
@@ -4579,8 +4590,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* this DMA buffer right after the DMA engine completely finishes the
* full buffer transmission.
*/
stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, skb_headlen(skb),
false);
stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_headlen(skb), false);
/* Prepare fragments */
for (i = 0; i < nfrags; i++) {
@@ -4590,14 +4600,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
goto dma_map_err;
goto error_dma_unmap;
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
stmmac_tso_allocator(priv, &entry, des, skb_frag_size(frag),
(i == nfrags - 1), queue);
stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des,
stmmac_set_tx_skb_dma_entry(tx_q, entry, des,
skb_frag_size(frag), true);
}
tx_q->cur_tx = entry;
stmmac_set_tx_dma_last_segment(tx_q, tx_q->cur_tx);
@@ -4702,7 +4713,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
dma_map_err:
error_dma_unmap:
for (;;) {
desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
first_entry);
if (first_entry == entry)
break;
first_entry = STMMAC_NEXT_ENTRY(first_entry,
priv->dma_conf.dma_tx_size);
}
error:
dev_err(priv->device, "Tx dma map failed\n");
dev_kfree_skb(skb);
priv->xstats.tx_dropped++;

View File

@@ -318,6 +318,11 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,
static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
unsigned int hdrlen, unsigned int role, __u16 inner_proto)
{
if (skb_is_gso(skb)) {
netdev_dbg(pctx->dev, "GSO is not supported in GTP\n");
goto err;
}
if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) {
netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
return 1;

View File

@@ -848,7 +848,6 @@ static int ipvlan_device_event(struct notifier_block *unused,
__ipvlan_link_delete(net, ipvlan->dev, &lst_kill);
}
unregister_netdevice_many(&lst_kill);
break;
}
case NETDEV_FEAT_CHANGE:
@@ -899,6 +898,9 @@ static int ipvlan_device_event(struct notifier_block *unused,
mutex_unlock(&port->pnodes_lock);
/* Avoid invoking nested netdevice notifiers under pnodes_lock. */
unregister_netdevice_many(&lst_kill);
ipvlan_port_put(port);
return ret;

View File

@@ -127,8 +127,10 @@ 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 pcpu_sw_netstats *tstats;
struct sk_buff *skb, *new_skb;
struct net_device *ndev;
unsigned long flags;
int rc;
ndev = dev->ndev;
@@ -139,17 +141,20 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len);
if (len < 0) {
ndev->stats.rx_errors++;
ndev->stats.rx_length_errors++;
DEV_STATS_INC(ndev, rx_errors);
DEV_STATS_INC(ndev, rx_length_errors);
goto enqueue_again;
}
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
tstats = this_cpu_ptr(ndev->tstats);
flags = u64_stats_update_begin_irqsave(&tstats->syncp);
u64_stats_inc(&tstats->rx_packets);
u64_stats_add(&tstats->rx_bytes, len);
u64_stats_update_end_irqrestore(&tstats->syncp, flags);
new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
if (!new_skb) {
ndev->stats.rx_dropped++;
DEV_STATS_INC(ndev, rx_dropped);
goto enqueue_again;
}
@@ -166,8 +171,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN);
if (rc) {
dev_kfree_skb_any(skb);
ndev->stats.rx_errors++;
ndev->stats.rx_fifo_errors++;
DEV_STATS_INC(ndev, rx_errors);
DEV_STATS_INC(ndev, rx_fifo_errors);
}
}
@@ -210,25 +215,39 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
{
struct ntb_netdev_queue *q = qp_data;
struct ntb_netdev *dev = q->ntdev;
struct pcpu_sw_netstats *tstats;
struct net_device *ndev;
struct sk_buff *skb;
unsigned long flags;
bool registered;
ndev = dev->ndev;
skb = data;
if (!skb || !ndev)
return;
rcu_read_lock();
registered = READ_ONCE(ndev->reg_state) == NETREG_REGISTERED;
if (!registered)
goto free_skb;
if (len > 0) {
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
/* The memcpy kthread can migrate, so pin the per-CPU update. */
tstats = get_cpu_ptr(ndev->tstats);
flags = u64_stats_update_begin_irqsave(&tstats->syncp);
u64_stats_inc(&tstats->tx_packets);
u64_stats_add(&tstats->tx_bytes, skb->len);
u64_stats_update_end_irqrestore(&tstats->syncp, flags);
put_cpu_ptr(ndev->tstats);
} else {
ndev->stats.tx_errors++;
ndev->stats.tx_aborted_errors++;
DEV_STATS_INC(ndev, tx_errors);
DEV_STATS_INC(ndev, tx_aborted_errors);
}
free_skb:
dev_kfree_skb_any(skb);
if (ntb_transport_tx_free_entry(qp) >= tx_start) {
if (registered && ntb_transport_tx_free_entry(qp) >= tx_start) {
/* Make sure anybody stopping the queue after this sees the new
* value of ntb_transport_tx_free_entry()
*/
@@ -237,6 +256,7 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
ntb_transport_link_query(q->qp))
netif_wake_subqueue(ndev, q->qid);
}
rcu_read_unlock();
}
static const struct ntb_queue_handlers ntb_netdev_handlers = {
@@ -277,7 +297,7 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
drop:
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
DEV_STATS_INC(ndev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -647,6 +667,7 @@ static int ntb_netdev_probe(struct device *client_dev)
}
ndev->features = NETIF_F_HIGHDMA;
ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;

View File

@@ -63,8 +63,6 @@ struct asyncppp {
struct tasklet_struct tsk;
refcount_t refcnt;
struct completion dead;
struct ppp_channel chan; /* interface to generic ppp layer */
unsigned char obuf[OBUFSIZE];
};
@@ -114,38 +112,6 @@ static const struct ppp_channel_ops async_ops = {
* Routines implementing the PPP line discipline.
*/
/*
* We have a potential race on dereferencing tty->disc_data,
* because the tty layer provides no locking at all - thus one
* cpu could be running ppp_asynctty_receive while another
* calls ppp_asynctty_close, which zeroes tty->disc_data and
* frees the memory that ppp_asynctty_receive is using. The best
* way to fix this is to use a rwlock in the tty struct, but for now
* we use a single global rwlock for all ttys in ppp line discipline.
*
* FIXME: this is no longer true. The _close path for the ldisc is
* now guaranteed to be sane.
*/
static DEFINE_RWLOCK(disc_data_lock);
static struct asyncppp *ap_get(struct tty_struct *tty)
{
struct asyncppp *ap;
read_lock(&disc_data_lock);
ap = tty->disc_data;
if (ap != NULL)
refcount_inc(&ap->refcnt);
read_unlock(&disc_data_lock);
return ap;
}
static void ap_put(struct asyncppp *ap)
{
if (refcount_dec_and_test(&ap->refcnt))
complete(&ap->dead);
}
/*
* Called when a tty is put into PPP line discipline. Called in process
* context.
@@ -180,9 +146,6 @@ ppp_asynctty_open(struct tty_struct *tty)
skb_queue_head_init(&ap->rqueue);
tasklet_setup(&ap->tsk, ppp_async_process);
refcount_set(&ap->refcnt, 1);
init_completion(&ap->dead);
ap->chan.private = ap;
ap->chan.ops = &async_ops;
ap->chan.mtu = PPP_MRU;
@@ -203,34 +166,18 @@ ppp_asynctty_open(struct tty_struct *tty)
}
/*
* Called when the tty is put into another line discipline
* or it hangs up. We have to wait for any cpu currently
* executing in any of the other ppp_asynctty_* routines to
* finish before we can call ppp_unregister_channel and free
* the asyncppp struct. This routine must be called from
* process context, not interrupt or softirq context.
* Called when the tty is put into another line discipline or it hangs up.
* This call is serialized against other ldisc functions.
*/
static void
ppp_asynctty_close(struct tty_struct *tty)
{
struct asyncppp *ap;
struct asyncppp *ap = tty->disc_data;
write_lock_irq(&disc_data_lock);
ap = tty->disc_data;
tty->disc_data = NULL;
write_unlock_irq(&disc_data_lock);
if (!ap)
return;
/*
* We have now ensured that nobody can start using ap from now
* on, but we have to wait for all existing users to finish.
* Note that ppp_unregister_channel ensures that no calls to
* our channel ops (i.e. ppp_async_send/ioctl) are in progress
* by the time it returns.
*/
if (!refcount_dec_and_test(&ap->refcnt))
wait_for_completion(&ap->dead);
tty->disc_data = NULL;
tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan);
@@ -240,17 +187,6 @@ ppp_asynctty_close(struct tty_struct *tty)
kfree(ap);
}
/*
* Called on tty hangup in process context.
*
* Wait for I/O to driver to complete and unregister PPP channel.
* This is already done by the close routine, so just call that.
*/
static void ppp_asynctty_hangup(struct tty_struct *tty)
{
ppp_asynctty_close(tty);
}
/*
* Read does nothing - no data is ever available this way.
* Pppd reads and writes packets via /dev/ppp instead.
@@ -281,7 +217,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,
static int
ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
struct asyncppp *ap = ap_get(tty);
struct asyncppp *ap = tty->disc_data;
int err, val;
int __user *p = (int __user *)arg;
@@ -322,7 +258,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
err = tty_mode_ioctl(tty, cmd, arg);
}
ap_put(ap);
return err;
}
@@ -331,7 +266,7 @@ static void
ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
size_t count)
{
struct asyncppp *ap = ap_get(tty);
struct asyncppp *ap = tty->disc_data;
unsigned long flags;
if (!ap)
@@ -341,21 +276,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
ap_put(ap);
tty_unthrottle(tty);
}
static void
ppp_asynctty_wakeup(struct tty_struct *tty)
{
struct asyncppp *ap = ap_get(tty);
struct asyncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (!ap)
return;
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
tasklet_schedule(&ap->tsk);
ap_put(ap);
}
@@ -365,7 +298,6 @@ static struct tty_ldisc_ops ppp_ldisc = {
.name = "ppp",
.open = ppp_asynctty_open,
.close = ppp_asynctty_close,
.hangup = ppp_asynctty_hangup,
.read = ppp_asynctty_read,
.write = ppp_asynctty_write,
.ioctl = ppp_asynctty_ioctl,

View File

@@ -38,11 +38,9 @@
#include <linux/ppp-ioctl.h>
#include <linux/ppp_channel.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/unaligned.h>
#include <linux/uaccess.h>
@@ -67,8 +65,6 @@ struct syncppp {
struct tasklet_struct tsk;
refcount_t refcnt;
struct completion dead_cmp;
struct ppp_channel chan; /* interface to generic ppp layer */
};
@@ -116,37 +112,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)
* Routines implementing the synchronous PPP line discipline.
*/
/*
* We have a potential race on dereferencing tty->disc_data,
* because the tty layer provides no locking at all - thus one
* cpu could be running ppp_synctty_receive while another
* calls ppp_synctty_close, which zeroes tty->disc_data and
* frees the memory that ppp_synctty_receive is using. The best
* way to fix this is to use a rwlock in the tty struct, but for now
* we use a single global rwlock for all ttys in ppp line discipline.
*
* FIXME: Fixed in tty_io nowadays.
*/
static DEFINE_RWLOCK(disc_data_lock);
static struct syncppp *sp_get(struct tty_struct *tty)
{
struct syncppp *ap;
read_lock(&disc_data_lock);
ap = tty->disc_data;
if (ap != NULL)
refcount_inc(&ap->refcnt);
read_unlock(&disc_data_lock);
return ap;
}
static void sp_put(struct syncppp *ap)
{
if (refcount_dec_and_test(&ap->refcnt))
complete(&ap->dead_cmp);
}
/*
* Called when a tty is put into sync-PPP line discipline.
*/
@@ -177,9 +142,6 @@ ppp_sync_open(struct tty_struct *tty)
skb_queue_head_init(&ap->rqueue);
tasklet_setup(&ap->tsk, ppp_sync_process);
refcount_set(&ap->refcnt, 1);
init_completion(&ap->dead_cmp);
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
ap->chan.mtu = PPP_MRU;
@@ -201,34 +163,18 @@ ppp_sync_open(struct tty_struct *tty)
}
/*
* Called when the tty is put into another line discipline
* or it hangs up. We have to wait for any cpu currently
* executing in any of the other ppp_synctty_* routines to
* finish before we can call ppp_unregister_channel and free
* the syncppp struct. This routine must be called from
* process context, not interrupt or softirq context.
* Called when the tty is put into another line discipline or it hangs up.
* This call is serialized against other ldisc functions.
*/
static void
ppp_sync_close(struct tty_struct *tty)
{
struct syncppp *ap;
struct syncppp *ap = tty->disc_data;
write_lock_irq(&disc_data_lock);
ap = tty->disc_data;
tty->disc_data = NULL;
write_unlock_irq(&disc_data_lock);
if (!ap)
return;
/*
* We have now ensured that nobody can start using ap from now
* on, but we have to wait for all existing users to finish.
* Note that ppp_unregister_channel ensures that no calls to
* our channel ops (i.e. ppp_sync_send/ioctl) are in progress
* by the time it returns.
*/
if (!refcount_dec_and_test(&ap->refcnt))
wait_for_completion(&ap->dead_cmp);
tty->disc_data = NULL;
tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan);
@@ -237,17 +183,6 @@ ppp_sync_close(struct tty_struct *tty)
kfree(ap);
}
/*
* Called on tty hangup in process context.
*
* Wait for I/O to driver to complete and unregister PPP channel.
* This is already done by the close routine, so just call that.
*/
static void ppp_sync_hangup(struct tty_struct *tty)
{
ppp_sync_close(tty);
}
/*
* Read does nothing - no data is ever available this way.
* Pppd reads and writes packets via /dev/ppp instead.
@@ -273,7 +208,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,
static int
ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
struct syncppp *ap = sp_get(tty);
struct syncppp *ap = tty->disc_data;
int __user *p = (int __user *)arg;
int err, val;
@@ -314,7 +249,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
break;
}
sp_put(ap);
return err;
}
@@ -323,7 +257,7 @@ static void
ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
size_t count)
{
struct syncppp *ap = sp_get(tty);
struct syncppp *ap = tty->disc_data;
unsigned long flags;
if (!ap)
@@ -333,21 +267,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
sp_put(ap);
tty_unthrottle(tty);
}
static void
ppp_sync_wakeup(struct tty_struct *tty)
{
struct syncppp *ap = sp_get(tty);
struct syncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (!ap)
return;
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
tasklet_schedule(&ap->tsk);
sp_put(ap);
}
@@ -357,7 +289,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = {
.name = "pppsync",
.open = ppp_sync_open,
.close = ppp_sync_close,
.hangup = ppp_sync_hangup,
.read = ppp_sync_read,
.write = ppp_sync_write,
.ioctl = ppp_synctty_ioctl,

View File

@@ -1446,6 +1446,7 @@ static const struct usb_device_id products[] = {
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0316, 3)}, /* Quectel RG255C */
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0112, 0)}, /* Fibocom FG132 */
{QMI_QUIRK_SET_DTR(0x04b7, 0x8217, 8)}, /* Compal EXM-G1x */
{QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */
{QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/
{QMI_FIXED_INTF(0x2692, 0x9025, 4)}, /* Cellient MPL200 (rebranded Qualcomm 05c6:9025) */

View File

@@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
return false;
}
if (ipv4_is_zeronet(nla_get_in_addr(attr))) {
NL_SET_ERR_MSG_MOD(extack, "IPv4 all-zeros source address is not allowed");
return false;
}
break;
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): {
@@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
return false;
}
if (ipv6_addr_any(&src)) {
NL_SET_ERR_MSG_MOD(extack, "IPv6 all-zeros source address is not allowed");
return false;
}
break;
}
#endif

View File

@@ -826,7 +826,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
fsm_deltimer(&grp->timer);
if (grp->saved_xid2->xid2_flag2 == 0x40) {
if (priv->xid->xid2_flag2 == 0x40) {
priv->xid->xid2_flag2 = 0x00;
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, 1,
@@ -1636,7 +1636,6 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
"The XID used in the MPC protocol is not valid, "
"rc = %d\n", rc);
priv->xid->xid2_flag2 = 0x40;
grp->saved_xid2->xid2_flag2 = 0x40;
}
return rc;

View File

@@ -57,20 +57,21 @@ struct ip_mc_socklist {
};
struct ip_sf_list {
struct ip_sf_list *sf_next;
struct ip_sf_list __rcu *sf_next;
unsigned long sf_count[2]; /* include/exclude counts */
__be32 sf_inaddr;
unsigned char sf_gsresp; /* include in g & s response? */
unsigned char sf_oldin; /* change state */
unsigned char sf_crcount; /* retrans. left to send */
struct rcu_head rcu;
};
struct ip_mc_list {
struct in_device *interface;
__be32 multiaddr;
unsigned int sfmode;
struct ip_sf_list *sources;
struct ip_sf_list *tomb;
struct ip_sf_list __rcu *sources;
struct ip_sf_list __rcu *tomb;
unsigned long sfcount[2];
union {
struct ip_mc_list *next;

View File

@@ -3082,6 +3082,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
return skb->transport_header != (typeof(skb->transport_header))~0U;
}
static inline void skb_unset_transport_header(struct sk_buff *skb)
{
skb->transport_header = (typeof(skb->transport_header))~0U;
}
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb));

View File

@@ -229,6 +229,9 @@ struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr,
struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src,
struct sockaddr_vm *dst,
struct net *net);
bool vsock_check_source(const struct vsock_sock *vsk,
const struct vsock_transport *transport,
const struct sockaddr_vm *src);
void vsock_remove_sock(struct vsock_sock *vsk);
void vsock_for_each_connected_socket(struct vsock_transport *transport,
void (*fn)(struct sock *sk));

View File

@@ -88,8 +88,6 @@ struct ip6_sf_socklist {
struct in6_addr sl_addr[] __counted_by(sl_max);
};
#define IP6_SFBLOCK 10 /* allocate this many at once */
struct ipv6_mc_socklist {
struct in6_addr addr;
int ifindex;

View File

@@ -704,7 +704,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,
static __inline__ void inet_reset_saddr(struct sock *sk)
{
inet_sk(sk)->inet_rcv_saddr = inet_sk(sk)->inet_saddr = 0;
inet_sk(sk)->inet_saddr = 0;
WRITE_ONCE(inet_sk(sk)->inet_rcv_saddr, 0);
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);

View File

@@ -765,8 +765,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,
void tcp_send_probe0(struct sock *);
int tcp_write_wakeup(struct sock *, int mib);
void tcp_send_fin(struct sock *sk);
void tcp_send_active_reset(struct sock *sk, gfp_t priority,
enum sk_rst_reason reason);
void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason);
int tcp_send_synack(struct sock *);
void tcp_push_one(struct sock *, unsigned int mss_now);
void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags);

View File

@@ -27,17 +27,20 @@ TRACE_EVENT(icmp_send,
TP_fast_assign(
struct iphdr *iph = ip_hdr(skb);
struct udphdr *uh = udp_hdr(skb);
int proto_4 = iph->protocol;
struct udphdr _uh, *uh = NULL;
__be32 *p32;
__entry->skbaddr = skb;
__entry->type = type;
__entry->code = code;
if (proto_4 != IPPROTO_UDP || (u8 *)uh < skb->head ||
(u8 *)uh + sizeof(struct udphdr)
> skb_tail_pointer(skb)) {
if (iph->protocol == IPPROTO_UDP)
uh = skb_header_pointer(skb,
skb_network_offset(skb) +
(iph->ihl << 2),
sizeof(_uh), &_uh);
if (!uh) {
__entry->sport = 0;
__entry->dport = 0;
__entry->ulen = 0;

View File

@@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev)
if (error)
BT_WARN("register suspend notifier failed error:%d\n", error);
queue_work(hdev->req_workqueue, &hdev->power_on);
idr_init(&hdev->adv_monitors_idr);
msft_register(hdev);
queue_work(hdev->req_workqueue, &hdev->power_on);
return id;
err_wqueue:

View File

@@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
struct l2cap_ecred_conn_data {
struct {
struct l2cap_ecred_conn_req_hdr req;
__le16 scid[5];
__le16 scid[L2CAP_ECRED_CONN_SCID_MAX];
} __packed pdu;
struct l2cap_chan *chan;
struct pid *pid;
@@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
if (chan == conn->chan)
return;
if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
if (!test_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
pid = chan->ops->get_peer_pid(chan);
@@ -1362,9 +1362,16 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
return;
if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
/* Unreachable, checked in l2cap_connect (+timer drops it if reached) */
if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid)))
return;
l2cap_ecred_init(chan, 0);
/* Set the same ident so we can match on the rsp */
@@ -3894,6 +3901,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
struct l2cap_ecred_conn_rsp *rsp_flex =
container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr);
if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
return;
/* Check if channel for outgoing connection or if it wasn't deferred
* since in those cases it must be skipped.
*/
@@ -3904,6 +3914,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
/* Reset ident so only one response is sent */
chan->ident = 0;
/* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */
if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid)))
rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM);
/* Include all channels pending with the same ident */
if (!rsp->pdu.rsp.result)
rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid);
@@ -5063,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
@@ -7369,6 +7384,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
goto done;
}
mutex_lock(&conn->lock);
l2cap_chan_lock(chan);
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
struct l2cap_chan_data data;
@@ -7376,19 +7394,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
data.pid = chan->ops->get_peer_pid(chan);
data.count = 1;
l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
__l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
/* Leave room for non-deferred channel that ends the group. */
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
data.count += 1;
/* Check if there isn't too many channels being connected */
if (data.count > L2CAP_ECRED_CONN_SCID_MAX) {
hci_conn_drop(hcon);
err = -EPROTO;
goto done;
goto chan_unlock;
}
}
mutex_lock(&conn->lock);
l2cap_chan_lock(chan);
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
hci_conn_drop(hcon);
err = -EBUSY;

View File

@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)
INIT_LIST_HEAD(&msft->handle_map);
INIT_LIST_HEAD(&msft->address_filters);
hdev->msft_data = msft;
mutex_init(&msft->filter_lock);
hdev->msft_data = msft;
}
void msft_release(struct hci_dev *hdev)

View File

@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
struct hlist_node *tmp;
rcu_assign_pointer(*pp, pg->next);
hlist_del_init(&pg->mglist);
/* Keep ->next (held under multicast_lock, freed later by the GC work):
* a port->mglist teardown walk may have latched this node as its next,
* and deleting other groups of the same port must not truncate it.
*/
hlist_del_init_rcu(&pg->mglist);
br_multicast_eht_clean_sets(pg);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent, false);
@@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br,
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
/* A teardown walk over port->mglist can reach a group that an earlier
* iteration already deleted as a side effect. It is off mp->ports by
* now, so skip it instead of falling through to the WARN_ON() below.
*/
if (hlist_unhashed(&pg->mglist))
return;
mp = br_mdb_ip_get(br, &pg->key.addr);
if (WARN_ON(!mp))
return;

View File

@@ -12703,7 +12703,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
const char *pat, int new_ifindex,
struct netlink_ext_ack *extack)
{
struct netdev_name_node *name_node;
struct netdev_name_node *name_node, *tmp;
struct net *net_old = dev_net(dev);
char new_name[IFNAMSIZ] = {};
int err, new_nsid;
@@ -12749,13 +12749,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
}
/* Check that none of the altnames conflicts. */
err = -EEXIST;
netdev_for_each_altname(dev, name_node) {
if (netdev_name_in_use(net, name_node->name)) {
NL_SET_ERR_MSG_FMT(extack,
"An interface with the altname %s exists in the target netns",
name_node->name);
goto out;
netdev_for_each_altname_safe(dev, name_node, tmp) {
if (!netdev_name_in_use(net, name_node->name))
continue;
if (!check_net(net_old)) {
__netdev_name_node_alt_destroy(name_node);
continue;
}
NL_SET_ERR_MSG_FMT(extack,
"An interface with the altname %s exists in the target netns",
name_node->name);
goto out;
}
/* Check that new_ifindex isn't used yet. */
@@ -13210,7 +13216,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
static void __net_exit default_device_exit_net(struct net *net)
{
struct netdev_name_node *name_node, *tmp;
struct net_device *dev, *aux;
/*
* Push all migratable network devices back to the
@@ -13234,10 +13239,6 @@ static void __net_exit default_device_exit_net(struct net *net)
if (netdev_name_in_use(&init_net, fb_name))
snprintf(fb_name, IFNAMSIZ, "dev%%d");
netdev_for_each_altname_safe(dev, name_node, tmp)
if (netdev_name_in_use(&init_net, name_node->name))
__netdev_name_node_alt_destroy(name_node);
err = dev_change_net_namespace(dev, &init_net, fb_name);
if (err) {
pr_emerg("%s: failed to move %s to init_net: %d\n",

View File

@@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
if (unlikely(!(dev->flags & IFF_UP)))
goto drop;
skb_unset_transport_header(skb);
if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
res = netif_rx(skb);
goto unlock;

View File

@@ -1073,7 +1073,8 @@ netmem_ref page_pool_alloc_frag_netmem(struct page_pool *pool,
if (WARN_ON(size > max_size))
return 0;
size = ALIGN(size, dma_get_cache_alignment());
size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(),
__alignof__(struct skb_shared_info)));
*offset = pool->frag_offset;
if (netmem && *offset + size > max_size) {

View File

@@ -2494,6 +2494,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
#ifdef CONFIG_BPF_SYSCALL
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
#endif
#if IS_ENABLED(CONFIG_INET_PSP)
RCU_INIT_POINTER(newsk->psp_assoc, NULL);
#endif
/* SANITY */
if (likely(newsk->sk_net_refcnt)) {

View File

@@ -874,7 +874,7 @@ static void fib_rebalance(struct fib_info *fi)
change_nexthops(fi) {
int upper_bound;
if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
upper_bound = -1;
} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {

View File

@@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im)
}
}
#define pmc_dereference(e, pmc) \
rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \
lockdep_is_held(&(pmc)->interface->mc_tomb_lock))
#define for_each_pmc_rcu(in_dev, pmc) \
for (pmc = rcu_dereference(in_dev->mc_list); \
pmc != NULL; \
@@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im)
pmc != NULL; \
pmc = rtnl_dereference(pmc->next_rcu))
#define for_each_psf_mclock(pmc, psf) \
for (psf = pmc_dereference((pmc)->sources, pmc); \
psf; \
psf = pmc_dereference(psf->sf_next, pmc))
#define for_each_psf_rcu(im, psf) \
for (psf = rcu_dereference((im)->sources); \
psf; \
psf = rcu_dereference(psf->sf_next))
#define for_each_psf_tomb(pmc, psf) \
for (psf = pmc_dereference((pmc)->tomb, pmc); \
psf; \
psf = pmc_dereference(psf->sf_next, pmc))
static void ip_sf_list_clear_all(struct ip_sf_list *psf)
{
struct ip_sf_list *next;
while (psf) {
next = psf->sf_next;
kfree(psf);
next = rcu_dereference_protected(psf->sf_next, 1);
kfree_rcu(psf, rcu);
psf = next;
}
}
@@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
struct ip_sf_list *psf;
int scount = 0;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (!is_in(pmc, psf, type, gdeleted, sdeleted))
continue;
scount++;
@@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
struct net *net = dev_net(dev);
struct igmpv3_report *pih;
struct igmpv3_grec *pgr = NULL;
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
struct ip_sf_list *psf, *psf_next, *psf_prev;
struct ip_sf_list __rcu **psf_list;
int scount, stotal, first, isquery, truncate;
unsigned int mtu;
@@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
if (!*psf_list)
if (!rcu_access_pointer(*psf_list))
goto empty_source;
pih = skb ? igmpv3_report_hdr(skb) : NULL;
@@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
}
first = 1;
psf_prev = NULL;
for (psf = *psf_list; psf; psf = psf_next) {
for (psf = pmc_dereference(*psf_list, pmc);
psf;
psf = psf_next) {
__be32 *psrc;
psf_next = psf->sf_next;
psf_next = pmc_dereference(psf->sf_next, pmc);
if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
psf_prev = psf;
@@ -583,10 +605,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf->sf_crcount--;
if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
if (psf_prev)
psf_prev->sf_next = psf->sf_next;
rcu_assign_pointer(psf_prev->sf_next,
psf_next);
else
*psf_list = psf->sf_next;
kfree(psf);
rcu_assign_pointer(*psf_list,
psf_next);
kfree_rcu(psf, rcu);
continue;
}
}
@@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
/*
* remove zero-count source records from a source filter list
*/
static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf)
{
struct ip_sf_list *psf_prev, *psf_next, *psf;
psf_prev = NULL;
for (psf = *ppsf; psf; psf = psf_next) {
psf_next = psf->sf_next;
for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) {
psf_next = rcu_dereference_protected(psf->sf_next, 1);
if (psf->sf_crcount == 0) {
if (psf_prev)
psf_prev->sf_next = psf->sf_next;
rcu_assign_pointer(psf_prev->sf_next, psf_next);
else
*ppsf = psf->sf_next;
kfree(psf);
} else
rcu_assign_pointer(*ppsf, psf_next);
kfree_rcu(psf, rcu);
} else {
psf_prev = psf;
}
}
}
static void kfree_pmc(struct ip_mc_list *pmc)
{
ip_sf_list_clear_all(pmc->sources);
ip_sf_list_clear_all(pmc->tomb);
ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1));
ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1));
kfree(pmc);
}
@@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev)
igmpv3_clear_zeros(&pmc->sources);
}
}
if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) &&
!rcu_access_pointer(pmc->sources)) {
if (pmc_prev)
pmc_prev->next = pmc_next;
else
@@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
int i, scount;
scount = 0;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++) {
@@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
/* mark INCLUDE-mode sources */
scount = 0;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++)
@@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
if (pmc->sfmode == MCAST_INCLUDE) {
struct ip_sf_list *psf;
for_each_psf_mclock(im, psf)
psf->sf_crcount = pmc->crcount;
pmc->tomb = im->tomb;
pmc->sources = im->sources;
im->tomb = im->sources = NULL;
for (psf = pmc->sources; psf; psf = psf->sf_next)
psf->sf_crcount = pmc->crcount;
RCU_INIT_POINTER(im->tomb, NULL);
RCU_INIT_POINTER(im->sources, NULL);
}
spin_unlock_bh(&im->lock);
@@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
if (pmc) {
im->interface = pmc->interface;
if (im->sfmode == MCAST_INCLUDE) {
swap(im->tomb, pmc->tomb);
swap(im->sources, pmc->sources);
for (psf = im->sources; psf; psf = psf->sf_next)
struct ip_sf_list *sources, *tomb;
tomb = rcu_replace_pointer(im->tomb,
rcu_dereference_protected(pmc->tomb, 1),
lockdep_is_held(&im->lock));
rcu_assign_pointer(pmc->tomb, tomb);
sources = rcu_replace_pointer(im->sources,
rcu_dereference_protected(pmc->sources, 1),
lockdep_is_held(&im->lock));
rcu_assign_pointer(pmc->sources, sources);
for_each_psf_mclock(im, psf)
psf->sf_crcount = in_dev->mr_qrv ?:
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
} else {
@@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
struct ip_sf_list *psf;
spin_lock_bh(&pmc->lock);
psf = pmc->tomb;
pmc->tomb = NULL;
psf = pmc_dereference(pmc->tomb, pmc);
RCU_INIT_POINTER(pmc->tomb, NULL);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(psf);
}
@@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
int rv = 0;
psf_prev = NULL;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* source filter not found, or count wrong => bug */
return -ESRCH;
}
psf->sf_count[sfmode]--;
WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1);
if (psf->sf_count[sfmode] == 0) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* no more filters for this source */
if (psf_prev)
psf_prev->sf_next = psf->sf_next;
rcu_assign_pointer(psf_prev->sf_next,
pmc_dereference(psf->sf_next, pmc));
else
pmc->sources = psf->sf_next;
rcu_assign_pointer(pmc->sources,
pmc_dereference(psf->sf_next, pmc));
#ifdef CONFIG_IP_MULTICAST
if (psf->sf_oldin &&
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
psf->sf_next = pmc->tomb;
pmc->tomb = psf;
rv = 1;
} else
struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
if (dpsf) {
*dpsf = *psf;
dpsf->sf_crcount = in_dev->mr_qrv ?:
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
rcu_assign_pointer(dpsf->sf_next,
pmc_dereference(pmc->tomb, pmc));
rcu_assign_pointer(pmc->tomb, dpsf);
rv = 1;
}
}
#endif
kfree(psf);
kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
err = -EINVAL;
if (!pmc->sfcount[sfmode])
goto out_unlock;
pmc->sfcount[sfmode]--;
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
}
err = 0;
for (i = 0; i < sfcount; i++) {
@@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#ifdef CONFIG_IP_MULTICAST
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
for (psf = pmc->sources; psf; psf = psf->sf_next)
for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(pmc->interface);
} else if (sf_setstate(pmc) || changerec) {
@@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
struct ip_sf_list *psf, *psf_prev;
psf_prev = NULL;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
if (!psf)
return -ENOBUFS;
psf->sf_inaddr = *psfsrc;
if (psf_prev) {
psf_prev->sf_next = psf;
} else
pmc->sources = psf;
if (psf_prev)
rcu_assign_pointer(psf_prev->sf_next, psf);
else
rcu_assign_pointer(pmc->sources, psf);
}
psf->sf_count[sfmode]++;
WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1);
if (psf->sf_count[sfmode] == 1) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc)
struct ip_sf_list *psf;
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
for (psf = pmc->sources; psf; psf = psf->sf_next)
for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
psf->sf_oldin = mca_xcount ==
psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
} else
} else {
psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
}
}
}
static int sf_setstate(struct ip_mc_list *pmc)
@@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc)
int new_in, rv;
rv = 0;
for (psf = pmc->sources; psf; psf = psf->sf_next) {
for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
} else
} else {
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
}
if (new_in) {
if (!psf->sf_oldin) {
struct ip_sf_list *prev = NULL;
for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
prev = dpsf;
}
if (dpsf) {
struct ip_sf_list *dpsf_next;
dpsf_next = pmc_dereference(dpsf->sf_next, pmc);
if (prev)
prev->sf_next = dpsf->sf_next;
rcu_assign_pointer(prev->sf_next, dpsf_next);
else
pmc->tomb = dpsf->sf_next;
kfree(dpsf);
rcu_assign_pointer(pmc->tomb, dpsf_next);
kfree_rcu(dpsf, rcu);
}
psf->sf_crcount = qrv;
rv++;
@@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc)
* add or update "delete" records if an active filter
* is now inactive
*/
for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
}
if (!dpsf) {
dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
if (!dpsf)
continue;
*dpsf = *psf;
/* pmc->lock held by callers */
dpsf->sf_next = pmc->tomb;
pmc->tomb = dpsf;
rcu_assign_pointer(dpsf->sf_next,
pmc_dereference(pmc->tomb, pmc));
rcu_assign_pointer(pmc->tomb, dpsf);
}
dpsf->sf_crcount = qrv;
rv++;
@@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#endif
isexclude = pmc->sfmode == MCAST_EXCLUDE;
if (!delta)
pmc->sfcount[sfmode]++;
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1);
err = 0;
for (i = 0; i < sfcount; i++) {
err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
@@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
int j;
if (!delta)
pmc->sfcount[sfmode]--;
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
for (j = 0; j < i; j++)
(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
@@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
for (psf = pmc->sources; psf; psf = psf->sf_next)
for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(in_dev);
} else if (sf_setstate(pmc)) {
@@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
struct ip_sf_list *tomb, *sources;
spin_lock_bh(&pmc->lock);
tomb = pmc->tomb;
pmc->tomb = NULL;
sources = pmc->sources;
pmc->sources = NULL;
tomb = pmc_dereference(pmc->tomb, pmc);
RCU_INIT_POINTER(pmc->tomb, NULL);
sources = pmc_dereference(pmc->sources, pmc);
RCU_INIT_POINTER(pmc->sources, NULL);
pmc->sfmode = MCAST_EXCLUDE;
pmc->sfcount[MCAST_INCLUDE] = 0;
pmc->sfcount[MCAST_EXCLUDE] = 1;
WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0);
WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(tomb);
@@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
rv = 1;
} else if (im) {
if (src_addr) {
spin_lock_bh(&im->lock);
for (psf = im->sources; psf; psf = psf->sf_next) {
for_each_psf_rcu(im, psf) {
if (psf->sf_inaddr == src_addr)
break;
}
if (psf)
rv = psf->sf_count[MCAST_INCLUDE] ||
psf->sf_count[MCAST_EXCLUDE] !=
im->sfcount[MCAST_EXCLUDE];
rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) ||
READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) !=
READ_ONCE(im->sfcount[MCAST_EXCLUDE]);
else
rv = im->sfcount[MCAST_EXCLUDE] != 0;
spin_unlock_bh(&im->lock);
} else
rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0;
} else {
rv = 1; /* unspecified source; tentatively allow */
}
}
return rv;
}
@@ -3043,7 +3095,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
im = rcu_dereference(idev->mc_list);
if (likely(im)) {
spin_lock_bh(&im->lock);
psf = im->sources;
psf = pmc_dereference(im->sources, im);
if (likely(psf)) {
state->im = im;
state->idev = idev;
@@ -3059,7 +3111,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
{
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
psf = psf->sf_next;
psf = pmc_dereference(psf->sf_next, state->im);
while (!psf) {
spin_unlock_bh(&state->im->lock);
state->im = state->im->next;
@@ -3075,7 +3127,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
state->im = rcu_dereference(state->idev->mc_list);
}
spin_lock_bh(&state->im->lock);
psf = state->im->sources;
psf = pmc_dereference(state->im->sources, state->im);
}
out:
return psf;

View File

@@ -3182,8 +3182,7 @@ void __tcp_close(struct sock *sk, long timeout)
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation,
SK_RST_REASON_TCP_ABORT_ON_CLOSE);
tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, 0);
@@ -3257,7 +3256,7 @@ void __tcp_close(struct sock *sk, long timeout)
struct tcp_sock *tp = tcp_sk(sk);
if (READ_ONCE(tp->linger2) < 0) {
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, GFP_ATOMIC,
tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_LINGER);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONLINGER);
@@ -3276,7 +3275,7 @@ void __tcp_close(struct sock *sk, long timeout)
if (sk->sk_state != TCP_CLOSE) {
if (tcp_check_oom(sk, 0)) {
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, GFP_ATOMIC,
tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONMEMORY);
@@ -3377,14 +3376,14 @@ int tcp_disconnect(struct sock *sk, int flags)
} else if (unlikely(tp->repair)) {
WRITE_ONCE(sk->sk_err, ECONNABORTED);
} else if (tcp_need_reset(old_state)) {
tcp_send_active_reset(sk, gfp_any(), SK_RST_REASON_TCP_STATE);
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (tp->snd_nxt != tp->write_seq &&
(1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) {
/* The last check adjusts for discrepancy of Linux wrt. RFC
* states
*/
tcp_send_active_reset(sk, gfp_any(),
tcp_send_active_reset(sk,
SK_RST_REASON_TCP_DISCONNECT_WITH_DATA);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (old_state == TCP_SYN_SENT)
@@ -4562,9 +4561,11 @@ int do_tcp_getsockopt(struct sock *sk, int level,
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
ca_ops = icsk->icsk_ca_ops;
rcu_read_lock();
ca_ops = READ_ONCE(icsk->icsk_ca_ops);
if (ca_ops && ca_ops->get_info)
sz = ca_ops->get_info(sk, ~0U, &attr, &info);
rcu_read_unlock();
len = min_t(unsigned int, len, sz);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
@@ -4577,16 +4578,24 @@ int do_tcp_getsockopt(struct sock *sk, int level,
val = !inet_csk_in_pingpong_mode(sk);
break;
case TCP_CONGESTION:
case TCP_CONGESTION: {
char ca_name[TCP_CA_NAME_MAX] = {};
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
len = min_t(unsigned int, len, TCP_CA_NAME_MAX);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
return -EFAULT;
if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len))
rcu_read_lock();
memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name,
sizeof(ca_name));
rcu_read_unlock();
if (copy_to_sockptr(optval, ca_name, len))
return -EFAULT;
return 0;
}
case TCP_ULP:
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
@@ -5147,8 +5156,7 @@ int tcp_abort(struct sock *sk, int err)
bh_lock_sock(sk);
if (tcp_need_reset(sk->sk_state))
tcp_send_active_reset(sk, GFP_ATOMIC,
SK_RST_REASON_TCP_STATE);
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
tcp_done_with_error(sk, err);
bh_unlock_sock(sk);

View File

@@ -223,7 +223,7 @@ void tcp_assign_congestion_control(struct sock *sk)
ca = rcu_dereference(net->ipv4.tcp_congestion_control);
if (unlikely(!bpf_try_module_get(ca, ca->owner)))
ca = &tcp_reno;
icsk->icsk_ca_ops = ca;
WRITE_ONCE(icsk->icsk_ca_ops, ca);
rcu_read_unlock();
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
@@ -253,7 +253,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_cleanup_congestion_control(sk);
icsk->icsk_ca_ops = ca;
WRITE_ONCE(icsk->icsk_ca_ops, ca);
icsk->icsk_ca_setsockopt = 1;
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));

View File

@@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struct sock *sk)
/* No ECN support? Fall back to Reno. Also need to clear
* ECT from sk since it is set during 3WHS for DCTCP.
*/
inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno);
INET_ECN_dontxmit(sk);
}
@@ -228,7 +228,7 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr,
if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
memset(&info->dctcp, 0, sizeof(info->dctcp));
if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) {
info->dctcp.dctcp_enabled = 1;
info->dctcp.dctcp_ce_state = (u16) ca->ce_state;
info->dctcp.dctcp_alpha = ca->dctcp_alpha;

View File

@@ -507,7 +507,7 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst)
ca = tcp_ca_find_key(ca_key);
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
icsk->icsk_ca_ops = ca;
WRITE_ONCE(icsk->icsk_ca_ops, ca);
ca_got_dst = true;
}
rcu_read_unlock();

View File

@@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
flush |= skb->ip_summed != p->ip_summed;
flush |= skb->csum_level != p->csum_level;
flush |= NAPI_GRO_CB(p)->count >= 64;
flush |= NAPI_GRO_CB(p)->is_flist != NAPI_GRO_CB(skb)->is_flist;
skb_set_network_header(skb, skb_gro_receive_network_offset(skb));
if (flush || skb_gro_receive_list(p, skb))
@@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
struct net *net;
int iif, sdif;
if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
return;
p = tcp_gro_lookup(head, th);
if (p) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
/* flist GRO applies to consecutive non-GSO skbs */
if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
return;
}
/* Fall back to the regular GRO path */
if (NAPI_GRO_CB(p)->count == 1)
NAPI_GRO_CB(p)->is_flist = 0;
NAPI_GRO_CB(skb)->is_flist = 0;
return;
}
@@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet_lookup_established(net, iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
iif, sdif);
NAPI_GRO_CB(skb)->is_flist = !sk;
NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
}
@@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
if (!th)
goto flush;
tcp4_check_fraglist_gro(head, skb, th);
if (unlikely(skb->dev->features & NETIF_F_GRO_FRAGLIST))
tcp4_check_fraglist_gro(head, skb, th);
return tcp_gro_receive(head, skb, th);

View File

@@ -3849,9 +3849,9 @@ void tcp_send_fin(struct sock *sk)
* was unread data in the receive queue. This behavior is recommended
* by RFC 2525, section 2.17. -DaveM
*/
void tcp_send_active_reset(struct sock *sk, gfp_t priority,
enum sk_rst_reason reason)
void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason)
{
gfp_t priority = sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN);
struct sk_buff *skb;
TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
@@ -4092,7 +4092,7 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst)
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
icsk->icsk_ca_ops = ca;
WRITE_ONCE(icsk->icsk_ca_ops, ca);
}
rcu_read_unlock();
}

View File

@@ -126,7 +126,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
(!tp->snd_wnd && !tp->packets_out))
do_reset = true;
if (do_reset)
tcp_send_active_reset(sk, GFP_ATOMIC,
tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
tcp_done(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);
@@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
goto out;
}
}
tcp_send_active_reset(sk, GFP_ATOMIC, SK_RST_REASON_TCP_STATE);
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
goto death;
}
@@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
icsk->icsk_probes_out > 0) ||
(user_timeout == 0 &&
icsk->icsk_probes_out >= keepalive_probes(tp))) {
tcp_send_active_reset(sk, GFP_ATOMIC,
tcp_send_active_reset(sk,
SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT);
tcp_write_err(sk);
goto out;

View File

@@ -900,6 +900,15 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
return sk;
}
static void udp_err_update_exception(struct net *net, struct sk_buff *skb,
int type, int code, u32 info)
{
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
ipv4_update_pmtu(skb, net, info, 0, IPPROTO_UDP);
else if (type == ICMP_REDIRECT)
ipv4_redirect(skb, net, 0, IPPROTO_UDP);
}
/*
* This routine is called by the ICMP module when it gets some
* sort of error condition. If err < 0 then the socket should
@@ -923,6 +932,8 @@ int udp_err(struct sk_buff *skb, u32 info)
int harderr;
int err;
udp_err_update_exception(net, skb, type, code, info);
uh = (struct udphdr *)(skb->data + (iph->ihl << 2));
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
iph->saddr, uh->source, skb->dev->ifindex,
@@ -2166,10 +2177,10 @@ int __udp_disconnect(struct sock *sk, int flags)
*/
sk->sk_state = TCP_CLOSE;
inet->inet_daddr = 0;
WRITE_ONCE(inet->inet_daddr, 0);
inet->inet_dport = 0;
sock_rps_reset_rxhash(sk);
sk->sk_bound_dev_if = 0;
WRITE_ONCE(sk->sk_bound_dev_if, 0);
if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) {
inet_reset_saddr(sk);
if (sk->sk_prot->rehash &&

View File

@@ -445,7 +445,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
hdr->segments_left--;
addr = hdr->segments + hdr->segments_left;
skb_push(skb, sizeof(struct ipv6hdr));
skb_push(skb, -skb_network_offset(skb));
if (skb->ip_summed == CHECKSUM_COMPLETE)
seg6_update_csum(skb);
@@ -469,7 +469,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
}
ipv6_hdr(skb)->hop_limit--;
skb_pull(skb, sizeof(struct ipv6hdr));
skb_pull(skb, skb_transport_offset(skb));
goto looped_back;
}

View File

@@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
__be16 payload_protocol;
int ret;
@@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
goto tx_err;
if (t->parms.collect_md)
tun_info = skb_tunnel_info_txcheck(skb);
payload_protocol = skb_protocol(skb, true);
switch (payload_protocol) {
case htons(ETH_P_IP):
@@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
tx_err:
if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
kfree_skb(skb);

View File

@@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
return err;
}
mc_lst->next = np->ipv6_mc_list;
rcu_assign_pointer(mc_lst->next,
sock_dereference(np->ipv6_mc_list, sk));
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
return 0;
@@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
*lnk = mc_lst->next;
rcu_assign_pointer(*lnk,
sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
return 0;
}
@@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk)
struct ipv6_mc_socklist *mc_lst;
while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) {
np->ipv6_mc_list = mc_lst->next;
rcu_assign_pointer(np->ipv6_mc_list,
sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
}
}
@@ -355,12 +358,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
{
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct in6_addr *source, *group;
struct ip6_sf_socklist *newpsl, *psl;
struct net *net = sock_net(sk);
struct ipv6_mc_socklist *pmc;
struct ip6_sf_socklist *psl;
struct inet6_dev *idev;
int leavegroup = 0;
int i, j, rv;
int i, j;
int err;
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
@@ -409,13 +412,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (!add) {
if (!psl)
goto done; /* err = -EADDRNOTAVAIL */
rv = !0;
for (i = 0; i < psl->sl_count; i++) {
rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
if (rv == 0)
if (ipv6_addr_equal(&psl->sl_addr[i], source))
break;
}
if (rv) /* source not found */
if (i == psl->sl_count) /* source not found */
goto done; /* err = -EADDRNOTAVAIL */
/* special case - (INCLUDE, empty) == LEAVE_GROUP */
@@ -424,58 +425,74 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
goto done;
}
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
&sk->sk_omem_alloc);
if (psl->sl_count == 1) {
newpsl = NULL;
} else {
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr,
psl->sl_count - 1),
GFP_KERNEL);
if (!newpsl) {
atomic_add(struct_size(psl, sl_addr, psl->sl_max),
&sk->sk_omem_alloc);
err = -ENOBUFS;
goto done;
}
newpsl->sl_max = psl->sl_count - 1;
newpsl->sl_count = psl->sl_count - 1;
for (j = 0; j < i; j++)
newpsl->sl_addr[j] = psl->sl_addr[j];
for (j = i + 1; j < psl->sl_count; j++)
newpsl->sl_addr[j - 1] = psl->sl_addr[j];
}
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
for (j = i+1; j < psl->sl_count; j++)
psl->sl_addr[j-1] = psl->sl_addr[j];
psl->sl_count--;
rcu_assign_pointer(pmc->sflist, newpsl);
kfree_rcu(psl, rcu);
err = 0;
goto done;
}
/* else, add a new source to the filter */
if (psl && psl->sl_count >= sysctl_mld_max_msf) {
if (psl && psl->sl_count >= READ_ONCE(sysctl_mld_max_msf)) {
err = -ENOBUFS;
goto done;
}
if (!psl || psl->sl_count == psl->sl_max) {
struct ip6_sf_socklist *newpsl;
int count = IP6_SFBLOCK;
if (psl) {
for (i = 0; i < psl->sl_count; i++) {
if (ipv6_addr_equal(&psl->sl_addr[i], source))
goto done; /* err = -EADDRNOTAVAIL */
}
}
if (psl)
count += psl->sl_max;
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count),
GFP_KERNEL);
if (!newpsl) {
err = -ENOBUFS;
goto done;
}
newpsl->sl_max = count;
newpsl->sl_count = count - IP6_SFBLOCK;
if (psl) {
for (i = 0; i < psl->sl_count; i++)
newpsl->sl_addr[i] = psl->sl_addr[i];
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
&sk->sk_omem_alloc);
}
rcu_assign_pointer(pmc->sflist, newpsl);
kfree_rcu(psl, rcu);
psl = newpsl;
i = psl ? psl->sl_count + 1 : 1;
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, i),
GFP_KERNEL);
if (!newpsl) {
err = -ENOBUFS;
goto done;
}
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
for (i = 0; i < psl->sl_count; i++) {
rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
if (rv == 0) /* There is an error in the address. */
goto done;
newpsl->sl_max = i;
newpsl->sl_count = i;
if (psl) {
for (j = 0; j < psl->sl_count; j++)
newpsl->sl_addr[j] = psl->sl_addr[j];
}
for (j = psl->sl_count-1; j >= i; j--)
psl->sl_addr[j+1] = psl->sl_addr[j];
psl->sl_addr[i] = *source;
psl->sl_count++;
err = 0;
newpsl->sl_addr[i - 1] = *source;
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
if (psl)
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
&sk->sk_omem_alloc);
rcu_assign_pointer(pmc->sflist, newpsl);
kfree_rcu(psl, rcu);
err = 0;
done:
mutex_unlock(&idev->mc_lock);
in6_dev_put(idev);
@@ -784,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
if (!pmc)
return;
if (pmc_prev)
rcu_assign_pointer(pmc_prev->next, pmc->next);
rcu_assign_pointer(pmc_prev->next,
mc_dereference(pmc->next, idev));
else
rcu_assign_pointer(idev->mc_tomb, pmc->next);
rcu_assign_pointer(idev->mc_tomb,
mc_dereference(pmc->next, idev));
im->idev = pmc->idev;
if (im->mca_sfmode == MCAST_INCLUDE) {
@@ -966,7 +985,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
return -ENOMEM;
}
rcu_assign_pointer(mc->next, idev->mc_list);
rcu_assign_pointer(mc->next, mc_dereference(idev->mc_list, idev));
rcu_assign_pointer(idev->mc_list, mc);
mld_del_delrec(idev, mc);
@@ -1000,7 +1019,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
WRITE_ONCE(ma->mca_users, new_users);
if (new_users == 0) {
*map = ma->next;
rcu_assign_pointer(*map,
mc_dereference(ma->next, idev));
igmp6_group_dropped(ma);
inet6_ifmcaddr_notify(idev->dev, ma,
@@ -2351,14 +2371,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) &&
!mld_in_v1_mode(idev)) {
psf->sf_crcount = idev->mc_qrv;
rcu_assign_pointer(psf->sf_next,
mc_dereference(pmc->mca_tomb, idev));
rcu_assign_pointer(pmc->mca_tomb, psf);
rv = 1;
} else {
kfree_rcu(psf, rcu);
struct ip6_sf_list *dpsf = kmalloc_obj(*dpsf);
if (dpsf) {
*dpsf = *psf;
dpsf->sf_crcount = idev->mc_qrv;
rcu_assign_pointer(dpsf->sf_next,
mc_dereference(pmc->mca_tomb, idev));
rcu_assign_pointer(pmc->mca_tomb, dpsf);
rv = 1;
}
}
kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2621,7 +2645,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
static void igmp6_join_group(struct ifmcaddr6 *ma)
{
unsigned long delay;
unsigned long delay, interval;
mc_assert_locked(ma->idev);
@@ -2630,13 +2654,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
delay = get_random_u32_below(unsolicited_report_interval(ma->idev));
interval = unsolicited_report_interval(ma->idev);
delay = interval;
if (cancel_delayed_work(&ma->mca_work)) {
refcount_dec(&ma->mca_refcnt);
delay = ma->mca_work.timer.expires - jiffies;
}
if (delay >= interval)
delay = get_random_u32_below(interval);
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
refcount_inc(&ma->mca_refcnt);
WRITE_ONCE(ma->mca_flags, ma->mca_flags |
@@ -3001,7 +3029,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
unsigned int mca_flags = READ_ONCE(im->mca_flags);
unsigned long expires = READ_ONCE(im->mca_work.timer.expires);
long delta = READ_ONCE(im->mca_work.timer.expires) - jiffies;
seq_printf(seq,
"%-4d %-15s %pi6 %5d %08X %ld\n",
@@ -3009,7 +3037,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
&im->mca_addr,
READ_ONCE(im->mca_users), mca_flags,
(mca_flags & MAF_TIMER_RUNNING) ?
jiffies_to_clock_t(expires - jiffies) : 0);
jiffies_delta_to_clock_t(delta) : 0);
return 0;
}

View File

@@ -3255,7 +3255,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
ip6_redirect(skb, sock_net(sk), skb->dev->ifindex,
READ_ONCE(sk->sk_mark), sk_uid(sk));
}

View File

@@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
p = tcp_gro_lookup(head, th);
if (p) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
/* flist GRO applies to consecutive non-GSO skbs */
if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
return;
}
/* Fall back to the regular GRO path */
if (NAPI_GRO_CB(p)->count == 1)
NAPI_GRO_CB(p)->is_flist = 0;
NAPI_GRO_CB(skb)->is_flist = 0;
return;
}
@@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet6_lookup_established(net, &hdr->saddr, th->source,
&hdr->daddr, ntohs(th->dest),
iif, sdif);
NAPI_GRO_CB(skb)->is_flist = !sk;
NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
#endif /* IS_ENABLED(CONFIG_IPV6) */

View File

@@ -690,6 +690,17 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
return sk;
}
static void udpv6_err_update_exception(struct net *net, struct sk_buff *skb,
u8 type, __be32 info)
{
if (type == ICMPV6_PKT_TOOBIG)
ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0,
sock_net_uid(net, NULL));
else if (type == NDISC_REDIRECT)
ip6_redirect(skb, net, skb->dev->ifindex, 0,
sock_net_uid(net, NULL));
}
static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
@@ -703,6 +714,8 @@ static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int harderr;
int err;
udpv6_err_update_exception(net, skb, type, info);
daddr = seg6_get_daddr(skb, opt) ? : &hdr->daddr;
saddr = &hdr->saddr;
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,

View File

@@ -210,12 +210,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
phs_hdr->flags = flags;
if (flags == AF_IUCV_FLAG_SYN)
phs_hdr->window = iucv->msglimit;
else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
confirm_recv = atomic_read(&iucv->msg_recv);
phs_hdr->window = confirm_recv;
if (confirm_recv)
phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
}
memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
@@ -250,13 +244,22 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
}
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
/* Claim the receive credit here, not while building the header: every
* way this frame can be dropped has now been ruled out, so the window
* is zeroed only for as long as the transmit itself takes.
*/
if (flags == AF_IUCV_FLAG_WIN || !flags) {
confirm_recv = atomic_xchg(&iucv->msg_recv, 0);
phs_hdr->window = confirm_recv;
if (confirm_recv)
phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
}
atomic_inc(&iucv->skbs_in_xmit);
err = dev_queue_xmit(skb);
if (net_xmit_eval(err)) {
atomic_dec(&iucv->skbs_in_xmit);
} else {
atomic_sub(confirm_recv, &iucv->msg_recv);
WARN_ON(atomic_read(&iucv->msg_recv) < 0);
atomic_add(confirm_recv, &iucv->msg_recv);
}
return net_xmit_eval(err);
@@ -1241,6 +1244,7 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
struct iucv_sock *iucv = iucv_sk(sk);
unsigned int copied, rlen;
struct sk_buff *skb, *rskb, *cskb;
bool send_win = false;
int err = 0;
u32 offset;
@@ -1331,16 +1335,20 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
if (skb_queue_empty(&iucv->backlog_skb_q)) {
if (!list_empty(&iucv->message_q.list))
iucv_process_message_q(sk);
if (atomic_read(&iucv->msg_recv) >=
iucv->msglimit / 2) {
err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
if (err) {
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
}
}
if (iucv->transport == AF_IUCV_TRANS_HIPER &&
atomic_read(&iucv->msg_recv) >=
iucv->msglimit / 2)
send_win = true;
}
spin_unlock_bh(&iucv->message_q.lock);
if (send_win) {
err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
if (err) {
sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk);
}
}
}
done:

View File

@@ -76,7 +76,12 @@ struct ieee802154_local {
struct work_struct rx_mac_cmd_work;
/* Association */
struct ieee802154_pan_device *assoc_dev;
/* assoc_lock protects assoc_dev_extended_addr, assoc_addr,
* assoc_status, the assoc_done reinit/complete pairing and the
* IEEE802154_IS_ASSOCIATING bit in @ongoing.
*/
spinlock_t assoc_lock;
__le64 assoc_dev_extended_addr;
struct completion assoc_done;
__le16 assoc_addr;
u8 assoc_status;

View File

@@ -104,6 +104,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
init_completion(&local->assoc_done);
spin_lock_init(&local->assoc_lock);
/* init supported flags with 802.15.4 default ranges */
phy->supported.max_minbe = 8;

View File

@@ -536,7 +536,9 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
struct ieee802154_association_req_frame frame = {};
struct ieee802154_local *local = sdata->local;
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
__le16 resp_short_addr;
struct sk_buff *skb;
u8 resp_status;
int ret;
frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD;
@@ -578,9 +580,11 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
return ret;
}
local->assoc_dev = coord;
spin_lock(&local->assoc_lock);
reinit_completion(&local->assoc_done);
local->assoc_dev_extended_addr = coord->extended_addr;
set_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
spin_unlock(&local->assoc_lock);
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb);
if (ret) {
@@ -599,25 +603,37 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
goto clear_assoc;
}
if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY)
/* The association is complete: mac802154_process_association_resp()
* cleared the associating bit before waking us, so a second (e.g.
* malicious) ASSOC RESP can no longer pass the recheck and overwrite
* the result. Snapshot assoc_status/assoc_addr under the lock.
*/
spin_lock(&local->assoc_lock);
resp_status = local->assoc_status;
resp_short_addr = local->assoc_addr;
spin_unlock(&local->assoc_lock);
if (resp_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
if (resp_status == IEEE802154_PAN_AT_CAPACITY)
ret = -ERANGE;
else
ret = -EPERM;
dev_warn(&sdata->dev->dev,
"Negative ASSOC RESP received from %8phC: %s\n", &ceaddr,
local->assoc_status == IEEE802154_PAN_AT_CAPACITY ?
resp_status == IEEE802154_PAN_AT_CAPACITY ?
"PAN at capacity" : "access denied");
goto clear_assoc;
return ret;
}
ret = 0;
*short_addr = local->assoc_addr;
*short_addr = resp_short_addr;
return 0;
clear_assoc:
spin_lock(&local->assoc_lock);
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
local->assoc_dev = NULL;
spin_unlock(&local->assoc_lock);
return ret;
}
@@ -639,19 +655,28 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
dest->mode != IEEE802154_EXTENDED_ADDRESSING))
return -EINVAL;
if (unlikely(dest->extended_addr != wpan_dev->extended_addr ||
src->extended_addr != local->assoc_dev->extended_addr))
spin_lock(&local->assoc_lock);
if (unlikely(!test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing) ||
dest->extended_addr != wpan_dev->extended_addr ||
src->extended_addr != local->assoc_dev_extended_addr)) {
spin_unlock(&local->assoc_lock);
return -ENODEV;
}
memcpy(&resp_pl, skb->data, sizeof(resp_pl));
local->assoc_addr = resp_pl.short_addr;
local->assoc_status = resp_pl.status;
/* Clear the associating bit before waking the waiter: once the result
* is saved, any subsequent (e.g. malicious) ASSOC RESP must fail the
* test_bit() recheck above and can no longer overwrite the result.
*/
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
complete(&local->assoc_done);
spin_unlock(&local->assoc_lock);
dev_dbg(&skb->dev->dev,
"ASSOC RESP 0x%x received from %8phC, getting short address %04x\n",
local->assoc_status, &deaddr, local->assoc_addr);
complete(&local->assoc_done);
resp_pl.status, &deaddr, resp_pl.short_addr);
return 0;
}

View File

@@ -3109,8 +3109,7 @@ static void mptcp_do_fastclose(struct sock *sk)
*/
inet_csk(ssk)->icsk_ack.rcv_mss = TCP_MIN_MSS;
tcp_send_active_reset(ssk, ssk->sk_allocation,
SK_RST_REASON_TCP_ABORT_ON_CLOSE);
tcp_send_active_reset(ssk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
unlock:
release_sock(ssk);
}

View File

@@ -690,7 +690,7 @@ mptcp_send_active_reset_reason(struct sock *sk)
enum sk_rst_reason reason;
reason = sk_rst_convert_mptcp_reason(subflow->reset_reason);
tcp_send_active_reset(sk, GFP_ATOMIC, reason);
tcp_send_active_reset(sk, reason);
}
/* Made the fwd mem carried by the given skb available to the msk,

View File

@@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
int size_max, void **data)
{
union tpacket_uhdr ph;
int tp_len, off;
u32 tp_len;
int off;
ph.raw = frame;
@@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
break;
}
if (unlikely(tp_len > size_max)) {
pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
pr_err("packet size is too long (%u > %d)\n", tp_len, size_max);
return -EMSGSIZE;
}

View File

@@ -9,6 +9,7 @@
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <net/sock.h>
@@ -120,8 +121,10 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports);
* @nid: node id
* @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port
* @qrtr_tx_lock: lock for qrtr_tx_flow inserts
* @hello_sent: hello packet send successful
* @rx_queue: receive queue
* @item: list item for broadcast list
* @say_hello: delayed work for sending hello packet
*/
struct qrtr_node {
struct mutex ep_lock;
@@ -132,8 +135,11 @@ struct qrtr_node {
struct xarray qrtr_tx_flow;
struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */
bool hello_sent;
struct sk_buff_head rx_queue;
struct list_head item;
struct delayed_work say_hello;
};
/**
@@ -187,6 +193,8 @@ static void __qrtr_node_release(struct kref *kref)
list_del(&node->item);
mutex_unlock(&qrtr_node_lock);
cancel_delayed_work_sync(&node->say_hello);
skb_queue_purge(&node->rx_queue);
/* Free tx flow counters */
@@ -341,6 +349,14 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
size_t len = skb->len;
int rc, confirm_rx;
mutex_lock(&node->ep_lock);
if (!node->hello_sent && type != QRTR_TYPE_HELLO) {
mutex_unlock(&node->ep_lock);
kfree_skb(skb);
return -EAGAIN;
}
mutex_unlock(&node->ep_lock);
confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type);
if (confirm_rx < 0) {
kfree_skb(skb);
@@ -353,7 +369,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
hdr->src_node_id = cpu_to_le32(from->sq_node);
hdr->src_port_id = cpu_to_le32(from->sq_port);
if (to->sq_port == QRTR_PORT_CTRL) {
hdr->dst_node_id = cpu_to_le32(node->nid);
hdr->dst_node_id = cpu_to_le32(READ_ONCE(node->nid));
hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL);
} else {
hdr->dst_node_id = cpu_to_le32(to->sq_node);
@@ -372,6 +388,8 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
rc = node->ep->xmit(node->ep, skb);
else
kfree_skb(skb);
if (!rc && type == QRTR_TYPE_HELLO)
node->hello_sent = true;
mutex_unlock(&node->ep_lock);
}
/* Need to ensure that a subsequent message carries the otherwise lost
@@ -379,6 +397,9 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
if (rc && confirm_rx)
qrtr_tx_flow_failed(node, to->sq_node, to->sq_port);
if (rc == -EAGAIN && type == QRTR_TYPE_HELLO)
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
return rc;
}
@@ -416,7 +437,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
spin_lock_irqsave(&qrtr_nodes_lock, flags);
radix_tree_insert(&qrtr_nodes, nid, node);
if (node->nid == QRTR_EP_NID_AUTO)
node->nid = nid;
WRITE_ONCE(node->nid, nid);
spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
}
@@ -570,6 +591,38 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt,
return skb;
}
static void qrtr_hello_work(struct work_struct *work)
{
struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
struct qrtr_ctrl_pkt *pkt;
struct qrtr_node *node;
struct qrtr_sock *ctrl;
struct sk_buff *skb;
node = container_of(to_delayed_work(work), struct qrtr_node, say_hello);
/* NS must be bound before we can send; retry with backoff if not ready */
ctrl = qrtr_port_lookup(QRTR_PORT_CTRL);
if (!ctrl) {
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
return;
}
skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL);
if (!skb) {
qrtr_port_put(ctrl);
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
return;
}
pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO);
from.sq_node = qrtr_local_nid;
to.sq_node = node->nid;
qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to);
qrtr_port_put(ctrl);
}
/**
* qrtr_endpoint_register() - register a new endpoint
* @ep: endpoint to register
@@ -595,6 +648,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
node->nid = QRTR_EP_NID_AUTO;
node->ep = ep;
node->hello_sent = false;
INIT_DELAYED_WORK(&node->say_hello, qrtr_hello_work);
xa_init(&node->qrtr_tx_flow);
mutex_init(&node->qrtr_tx_lock);
@@ -605,6 +661,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
mutex_unlock(&qrtr_node_lock);
ep->node = node;
/* Initiate HELLO handshake from the core layer */
schedule_delayed_work(&node->say_hello, 0);
return 0;
}
EXPORT_SYMBOL_GPL(qrtr_endpoint_register);
@@ -879,6 +938,9 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
mutex_lock(&qrtr_node_lock);
list_for_each_entry(node, &qrtr_all_nodes, item) {
/* Skip nodes with no assigned node ID yet. */
if (READ_ONCE(node->nid) == QRTR_EP_NID_AUTO)
continue;
skbn = pskb_copy(skb, GFP_KERNEL);
if (!skbn)
break;

View File

@@ -212,6 +212,7 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
pr_err("failed to send lookup notification\n");
}
/* Announce the list of servers registered on the local node */
static int announce_servers(struct sockaddr_qrtr *sq)
{
struct qrtr_server *srv;
@@ -326,38 +327,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
return 0;
}
static int say_hello(struct sockaddr_qrtr *dest)
{
struct qrtr_ctrl_pkt pkt;
struct msghdr msg = { };
struct kvec iv;
int ret;
iv.iov_base = &pkt;
iv.iov_len = sizeof(pkt);
memset(&pkt, 0, sizeof(pkt));
pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
msg.msg_name = (struct sockaddr *)dest;
msg.msg_namelen = sizeof(*dest);
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
if (ret < 0)
pr_err("failed to send hello msg\n");
return ret;
}
/* Announce the list of servers registered on the local node */
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
{
int ret;
ret = say_hello(sq);
if (ret < 0)
return ret;
return announce_servers(sq);
}
@@ -774,10 +745,6 @@ int qrtr_ns_init(void)
qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
ret = say_hello(&qrtr_ns.bcast_sq);
if (ret < 0)
goto err_wq;
/* As the qrtr ns socket owner and creator is the same module, we have
* to decrease the qrtr module reference count to guarantee that it
* remains zero after the ns socket is created, otherwise, executing

View File

@@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
}
/*
* This is called by transports as they're bringing down a connection.
* It clears partial message state so that the transport can start sending
* and receiving over this connection again in the future. It is up to
* the transport to have serialized this call with its send and recv.
* This is called by rds_conn_shutdown() once the transport has brought
* a path down. It clears partial message state so that the transport
* can start sending and receiving over this path again in the future.
* The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call,
* which is what serializes it against the send and receive-refill
* paths.
*/
static void rds_conn_path_reset(struct rds_conn_path *cp)
{
@@ -120,7 +122,16 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
rds_stats_inc(s_conn_reset);
rds_send_path_reset(cp);
cp->cp_flags = 0;
/* Clear the bits the reset is responsible for individually: a
* blanket cp_flags = 0 is a plain store that can clobber a
* concurrent atomic read-modify-write on the same word.
* RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the
* caller, rds_conn_shutdown(), which releases them once the
* teardown is complete.
*/
clear_bit(RDS_LL_SEND_FULL, &cp->cp_flags);
clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
/* Do not clear next_rx_seq here, else we cannot distinguish
* retransmitted packets from new packets, and will hand all
@@ -406,28 +417,70 @@ void rds_conn_shutdown(struct rds_conn_path *cp)
}
mutex_unlock(&cp->cp_cm_lock);
/* Quiesce the transmit and receive-refill paths by
* acquiring their bit locks, not merely waiting for
* them to be released: with a plain wait, either path
* can re-take its lock the instant after we sample it
* clear and then run concurrently with the transport
* shutdown and the path reset below. Holding both
* locks across the teardown makes that structurally
* impossible.
*/
wait_event(cp->cp_waitq,
!test_bit(RDS_IN_XMIT, &cp->cp_flags));
!test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
wait_event(cp->cp_waitq,
!test_bit(RDS_RECV_REFILL, &cp->cp_flags));
!test_and_set_bit(RDS_RECV_REFILL, &cp->cp_flags));
conn->c_trans->conn_path_shutdown(cp);
rds_conn_path_reset(cp);
/* Release the two locks and wake any waiter (e.g.
* rds_tcp_reset_callbacks()) that blocked on them while
* we held them. The unlock orders the transport's ring
* re-initialization and the path reset above before
* either bit is seen clear. rds_conn_path_reset() leaves
* both bits alone: ownership ends here, not inside the
* reset.
*/
clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
clear_bit_unlock(RDS_RECV_REFILL, &cp->cp_flags);
wake_up_all(&cp->cp_waitq);
if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING,
RDS_CONN_DOWN) &&
!rds_conn_path_transition(cp, RDS_CONN_ERROR,
RDS_CONN_DOWN)) {
/* This can happen - eg when we're in the middle of tearing
* down the connection, and someone unloads the rds module.
* Quite reproducible with loopback connections.
* Mostly harmless.
/* The path was dropped again while we tore it
* down: by a socket state-change callback in
* irq context on receipt of a FIN, or by an
* accept that claimed the path just before a
* drop put it back to RDS_CONN_ERROR and then
* installed a fresh socket on it. Unless a
* pending destroy suppressed it, the drop also
* queued another shutdown pass, and that pass
* must run, because it is what tears down
* whatever attached to the path after the
* transport shutdown above sampled its state.
* Consuming the RDS_CONN_ERROR here would turn
* that pass into a no-op: leave the state
* alone, and let the pass finish the job.
*
* Note that this also happens with rds-tcp because
* we could have triggered rds_conn_path_drop in irq
* mode from rds_tcp_state change on the receipt of
* a FIN, thus we need to recheck for RDS_CONN_ERROR
* here.
* Quiesce the reconnect timer before bailing
* out, though. When a pending destroy did
* suppress the queue, no later pass runs, and
* rds_conn_path_destroy() is about to flush
* cp_down_w and free the path: it must not
* find cp_conn_w still armed. A successor
* pass, when there is one, re-arms the
* reconnect from its own tail.
*/
cancel_delayed_work_sync(&cp->cp_conn_w);
clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
if (rds_conn_path_state(cp) == RDS_CONN_ERROR)
return;
/* No current cp_state writer leaves a
* DISCONNECTING path in any state but
* RDS_CONN_ERROR; report loudly if one ever
* does.
*/
rds_conn_path_error(cp, "%s: failed to transition "
"to state DOWN, current state "

View File

@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)
static void release_refill(struct rds_connection *conn)
{
clear_bit(RDS_RECV_REFILL, &conn->c_flags);
smp_mb__after_atomic();
clear_bit_unlock(RDS_RECV_REFILL, &conn->c_flags);
/* We don't use wait_on_bit()/wake_up_bit() because our waking is in a
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
*/
if (waitqueue_active(&conn->c_waitq))
if (wq_has_sleeper(&conn->c_waitq))
wake_up_all(&conn->c_waitq);
}
@@ -392,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
/* the goal here is to just make sure that someone, somewhere
* is posting buffers. If we can't get the refill lock,
* let them do their thing
* let them do their thing. The holder may also be
* rds_conn_shutdown() tearing the path down, in which case
* there is nothing to post.
*/
if (!acquire_refill(conn))
return;

View File

@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
*
* wq_has_sleeper() supplies the full barrier that orders the wait
* queue read after the bit clear; clear_bit_unlock() alone is only
* a release and would let this check read a stale empty queue,
* losing the wake-up.
*/
if (waitqueue_active(&cp->cp_waitq))
if (wq_has_sleeper(&cp->cp_waitq))
wake_up_all(&cp->cp_waitq);
}
@@ -239,8 +244,11 @@ int rds_send_xmit(struct rds_conn_path *cp)
WRITE_ONCE(cp->cp_send_gen, send_gen);
/*
* rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT,
* we do the opposite to avoid races.
* rds_conn_shutdown() sets the conn state and then acquires
* RDS_IN_XMIT; we take the lock first and then check the state.
* Ownership is decided by the atomic RMW on the cp_flags word:
* if the teardown won the bit we back off here, and if we won
* it the teardown waits until we release it.
*/
if (!rds_conn_path_up(cp)) {
release_in_xmit(cp);

View File

@@ -115,46 +115,90 @@ void rds_tcp_restore_callbacks(struct socket *sock,
}
/*
* rds_tcp_reset_callbacks() switches the to the new sock and
* returns the existing tc->t_sock.
* rds_tcp_reset_callbacks() switches a path to a new socket and
* releases the old one it finds in tc->t_sock, resolving a duelling
* SYN.
*
* The only functions that set tc->t_sock are rds_tcp_set_callbacks
* and rds_tcp_reset_callbacks. Send and receive trust that
* it is set. The absence of RDS_CONN_UP bit protects those paths
* from being called while it isn't set.
* tc->t_sock is set by rds_tcp_set_callbacks() and cleared by
* rds_tcp_restore_callbacks(). Four paths write it: the active
* connect in rds_tcp_conn_path_connect(), which sets it and clears it
* again on failure; the accept path in rds_tcp_accept_one(), which
* sets it for a path with no socket yet; the teardown in
* rds_tcp_conn_path_shutdown(), which clears it; and the swap done
* here, which does both. The connect and accept paths are serialized
* against each other by t_conn_path_lock. Send and receive trust
* that it is set: the absence of RDS_CONN_UP protects those paths
* from being called while it isn't, and the swap done here runs under
* RDS_IN_XMIT so that it cannot interleave with a sender already
* inside rds_send_xmit().
*/
void rds_tcp_reset_callbacks(struct socket *sock,
struct rds_conn_path *cp)
{
struct rds_tcp_connection *tc = cp->cp_transport_data;
struct socket *osock = tc->t_sock;
if (!osock)
goto newsock;
struct socket *osock;
/* Need to resolve a duelling SYN between peers.
* We have an outstanding SYN to this peer, which may
* potentially have transitioned to the RDS_CONN_UP state,
* so we must quiesce any send threads before resetting
* cp_transport_data. We quiesce these threads by setting
* cp_state to something other than RDS_CONN_UP, and then
* waiting for any existing threads in rds_send_xmit to
* complete release_in_xmit(). (Subsequent threads entering
* rds_send_xmit() will bail on !rds_conn_up().
* cp_transport_data. Setting cp_state to something other
* than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT
* excludes any thread already inside rds_send_xmit() - or a
* teardown in rds_conn_shutdown(), which holds the same lock
* for the duration of the transport shutdown - for the whole
* socket swap and the rds_send_path_reset() below.
*
* However an incoming syn-ack at this point would end up
* marking the conn as RDS_CONN_UP, and would again permit
* rds_send_xmi() threads through, so ideally we would
* synchronize on RDS_CONN_UP after lock_sock(), but cannot
* do that: waiting on !RDS_IN_XMIT after lock_sock() may
* end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT
* would not get set. As a result, we set c_state to
* RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change
* cannot mark rds_conn_path_up() in the window before lock_sock()
* An incoming syn-ack at this point would end up marking the
* conn as RDS_CONN_UP, and would again permit rds_send_xmit()
* threads through, so ideally we would synchronize on
* RDS_CONN_UP after lock_sock(), but cannot do that: acquiring
* RDS_IN_XMIT after lock_sock() may end up deadlocking with
* tcp_sendmsg(), which takes the socket lock while holding
* RDS_IN_XMIT. As a result, we set c_state to
* RDS_CONN_RESETTING, to ensure that rds_tcp_state_change
* cannot mark rds_conn_path_up() in the window before
* lock_sock().
*
* Only make that transition if the path is still connecting
* (or already resetting from an earlier duel). A path in any
* other state - typically RDS_CONN_DISCONNECTING or
* RDS_CONN_ERROR with a shutdown in flight - is dropped
* instead. That still replaces its state, with RDS_CONN_ERROR,
* and, unless a pending destroy is about to reap the whole
* connection anyway, queues one more shutdown pass. A shutdown
* already in flight leaves that RDS_CONN_ERROR alone when it
* finishes; the queued pass then completes the transition to
* RDS_CONN_DOWN and tears down anything that attached to the
* path in the meantime.
*/
if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
RDS_CONN_RESETTING) &&
!rds_conn_path_transition(cp, RDS_CONN_RESETTING,
RDS_CONN_RESETTING))
rds_conn_path_drop(cp, 0);
wait_event(cp->cp_waitq,
!test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
/* Read t_sock only while owning RDS_IN_XMIT, never before the
* wait: the teardown in rds_conn_shutdown() releases the old
* socket and clears t_sock, so a pointer sampled earlier can
* be stale by the time we wake up. The teardown holds the
* same lock while it does so, so what we read here cannot
* change under us until we release it.
*/
osock = tc->t_sock;
if (!osock)
goto newsock;
/* reset receive side state for rds_tcp_data_recv() for osock.
*
* The sync cancels while owning RDS_IN_XMIT rely on cp_wq
* being ordered: a teardown blocked on the bit occupies
* cp_wq's only execution slot, so cp_send_w and cp_recv_w are
* pending at most and the cancels never flush. Nothing here
* may flush or wait on cp_wq itself.
*/
atomic_set(&cp->cp_state, RDS_CONN_RESETTING);
wait_event(cp->cp_waitq, !test_bit(RDS_IN_XMIT, &cp->cp_flags));
/* reset receive side state for rds_tcp_data_recv() for osock */
cancel_delayed_work_sync(&cp->cp_send_w);
cancel_delayed_work_sync(&cp->cp_recv_w);
lock_sock(osock->sk);
@@ -172,6 +216,9 @@ void rds_tcp_reset_callbacks(struct socket *sock,
lock_sock(sock->sk);
rds_tcp_set_callbacks(sock, cp);
release_sock(sock->sk);
clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
wake_up_all(&cp->cp_waitq);
}
/* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments

View File

@@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
if (rs_tcp->t_sock) {
/* Duelling SYN has been handled in rds_tcp_accept_one() */
rds_tcp_reset_callbacks(new_sock, cp);
/* rds_connect_path_complete() marks RDS_CONN_UP */
/* rds_connect_path_complete() marks RDS_CONN_UP, or,
* if a concurrent shutdown won the duel, drops the
* path again and the pass that drop queues reaps the
* socket installed above.
*/
rds_connect_path_complete(cp, RDS_CONN_RESETTING);
} else {
rds_tcp_set_callbacks(new_sock, cp);

View File

@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
+ nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
+ cookie_len /* TCA_ACT_COOKIE */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
/* TCA_ACT_USED_HW_STATS */
+ nla_total_size(sizeof(struct nla_bitfield32))
+ nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */
+ nla_total_size(0) /* TCA_ACT_STATS nested */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
/* TCA_STATS_BASIC */
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
/* TCA_STATS_PKT64 */
+ nla_total_size_64bit(sizeof(u64))
/* TCA_STATS_BASIC_HW */
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
/* TCA_STATS_PKT64, emitted by both of the basic copies above */
+ 2 * nla_total_size_64bit(sizeof(u64))
/* TCA_STATS_RATE_EST */
+ nla_total_size_64bit(sizeof(struct gnet_stats_rate_est))
/* TCA_STATS_RATE_EST64 */
+ nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64))
/* TCA_STATS_QUEUE */
+ nla_total_size_64bit(sizeof(struct gnet_stats_queue))
+ nla_total_size(0) /* TCA_ACT_OPTIONS nested */
@@ -1688,12 +1697,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
static int
tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
struct tc_action *actions[], int event,
struct tc_action *actions[], size_t attr_size, int event,
struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
@@ -1858,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
struct tc_action *action)
{
size_t attr_size = tcf_action_fill_size(action);
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
[0] = action,
};
struct sk_buff *skb;
size_t attr_size;
attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action));
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
@@ -1879,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
{
const struct tc_action_ops *ops = action->ops;
struct sk_buff *skb;
struct sk_buff *skb = NULL;
int ret;
if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
skb = NULL;
} else {
if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
skb = tcf_reoffload_del_notify_msg(net, action);
/* The action has already lost its hardware instance and is
* skip_sw, so it must be released whether or not the
* notification can be built. Drop the notification rather
* than leave an action behind that processes no packets.
*/
if (IS_ERR(skb))
return PTR_ERR(skb);
skb = NULL;
}
ret = tcf_idr_release_unsafe(action);
@@ -2044,7 +2058,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
attr_size = tcf_action_full_attrs_size(attr_size);
if (event == RTM_GETACTION)
ret = tcf_get_notify(net, portid, n, actions, event, extack);
ret = tcf_get_notify(net, portid, n, actions, attr_size, event,
extack);
else { /* delete */
ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
if (ret)

View File

@@ -1703,6 +1703,11 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
return -EINVAL;
}
nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) {
NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
return -EINVAL;
}
}
return 0;

View File

@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)
refcount_set(&root_ht->refcnt, 1);
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
if (root_ht->handle == 0) {
kfree(root_ht);
return -ENOMEM;
}
root_ht->prio = tp->prio;
root_ht->is_root = true;
idr_init(&root_ht->handle_idr);
@@ -695,21 +699,33 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
return ret;
}
static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid, int *err)
{
u32 index = htid | 0x800;
u32 max = htid | 0xFFF;
*err = 0;
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
index = htid + 1;
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
GFP_KERNEL))
index = max;
*err = idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
GFP_KERNEL);
if (*err)
return 0;
}
return index;
}
static int u32_kid_extack(int err, struct netlink_ext_ack *extack)
{
if (err == -ENOSPC)
NL_SET_ERR_MSG_MOD(extack, "Hash table node ID pool exhausted");
else
NL_SET_ERR_MSG_MOD(extack, "Failed to allocate node ID");
return err;
}
static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
[TCA_U32_CLASSID] = { .type = NLA_U32 },
[TCA_U32_HASH] = { .type = NLA_U32 },
@@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
* handle which is used to uniquely identify the match entry.
*/
if (!TC_U32_NODE(handle)) {
handle = gen_new_kid(ht, htid);
handle = gen_new_kid(ht, htid, &err);
if (err)
return u32_kid_extack(err, extack);
} else {
handle = htid | TC_U32_NODE(handle);
err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
/* The user did not give us a handle; lets just generate one
* from the table's pool of nodeids.
*/
handle = gen_new_kid(ht, htid);
handle = gen_new_kid(ht, htid, &err);
if (err)
return u32_kid_extack(err, extack);
}
if (tb[TCA_U32_SEL] == NULL) {

View File

@@ -212,8 +212,10 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
skb_pull(chunk->skb, sizeof(*ch));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
chunk->pdiscard = 1;
} else if (chunk->chunk_end + sizeof(*ch) <=
skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {

View File

@@ -3215,6 +3215,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc,
*errp = param.p;
switch (param.p->type) {
case SCTP_PARAM_ERR_CAUSE:
if (length < sizeof(struct sctp_addip_param) +
sizeof(struct sctp_errhdr))
return false;
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (length != sizeof(struct sctp_ipv4addr_param))
@@ -3448,20 +3451,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
case SCTP_PARAM_ERR_CAUSE:
length = sizeof(*asconf_ack_param);
err_param = (void *)asconf_ack_param + length;
asconf_ack_len -= length;
if (asconf_ack_len > 0)
return err_param->cause;
else
return SCTP_ERROR_INV_PARAM;
break;
return err_param->cause;
default:
return SCTP_ERROR_INV_PARAM;
}
}
length = ntohs(asconf_ack_param->param_hdr.length);
asconf_ack_param = (void *)asconf_ack_param + length;
asconf_ack_len -= length;
asconf_ack_param = (void *)asconf_ack_param + SCTP_PAD4(length);
asconf_ack_len -= SCTP_PAD4(length);
}
return err_code;

View File

@@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
timeout = asoc->timeouts[cmd->obj.to];
BUG_ON(!timeout);
/*
* SCTP has a hard time with timer starts. Because we process
* timer starts as side effects, it can be hard to tell if we
* have already started a timer or not, which leads to BUG
* halts when we call add_timer. So here, instead of just starting
* a timer, if the timer is already started, and just mod
* the timer with the shorter of the two expiration times
*/
if (!timer_pending(timer))
if (!timer_reduce(timer, jiffies + timeout))
sctp_association_hold(asoc);
timer_reduce(timer, jiffies + timeout);
break;
case SCTP_CMD_TIMER_RESTART:

View File

@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
self_str, if_name, peer_str);
strcpy(l->if_name, if_name);
strscpy(l->if_name, if_name);
l->addr = peer;
l->peer_caps = peer_caps;
l->net = net;
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
peer_str);
} else {
strcpy(l->name, tipc_bclink_name);
strscpy(l->name, tipc_bclink_name);
}
trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
tipc_link_reset(l);
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
msg_set_dest_session(hdr, l->peer_session);
}
msg_set_max_pkt(hdr, l->advertised_mtu);
strcpy(data, l->if_name);
memcpy(data, l->if_name, TIPC_MAX_IF_NAME);
msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
}

View File

@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua,
struct tipc_socket_addr *sk, u32 key)
{
struct name_table *nt = tipc_name_table(net);
u32 max_user_pub = TIPC_MAX_PUBL - 1;
struct tipc_net *tn = tipc_net(net);
struct publication *p = NULL;
struct sk_buff *skb = NULL;
bool protocol_type = false;
u32 rc_dests;
spin_lock_bh(&tn->nametbl_lock);
if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE ||
ua->sr.type == TIPC_TOP_SRV)
protocol_type = true;
if (nt->local_publ_count >= TIPC_MAX_PUBL) {
pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL);
spin_lock_bh(&tn->nametbl_lock);
if (protocol_type)
goto insert;
/* Reserve one entry for node state service type because it has cluster
* scope and it is distributed in bulk. So, the maximum number of user's
* publications is (TIPC_MAX_PUBL - 1).
*/
if (nt->local_publ_count >= max_user_pub) {
pr_warn("Bind failed, max limit %u reached\n", max_user_pub);
goto exit;
}
insert:
p = tipc_nametbl_insert_publ(net, ua, sk, key);
if (p) {
nt->local_publ_count++;
/* Not count node state, link state and topology server types
* so that maximum nt->local_publ_count does not prevent
* protocol service types from being inserted into the name
* table.
*/
if (!protocol_type)
nt->local_publ_count++;
skb = tipc_named_publish(net, p);
}
rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua,
p = tipc_nametbl_remove_publ(net, ua, sk, key);
if (p) {
nt->local_publ_count--;
if (p->sr.type != TIPC_NODE_STATE &&
p->sr.type != TIPC_LINK_STATE &&
p->sr.type != TIPC_TOP_SRV)
nt->local_publ_count--;
skb = tipc_named_withdraw(net, p);
list_del_init(&p->binding_sock);
kfree_rcu(p, rcu);

View File

@@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n)
pr_warn("Resetting all links to %x\n", n->addr);
tipc_node_write_lock(n);
trace_tipc_node_reset_links(n, true, " ");
tipc_node_write_unlock_fast(n);
for (i = 0; i < MAX_BEARERS; i++) {
tipc_node_link_down(n, i, false);
}

View File

@@ -438,6 +438,38 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
}
EXPORT_SYMBOL_GPL(vsock_find_connected_socket);
/**
* vsock_check_source - validate a packet source against a socket peer
* @vsk: socket receiving the packet
* @transport: transport receiving the packet
* @src: source address from the packet
*
* Return: true if the packet arrived on the socket's assigned transport and
* its source matches the stored peer. Loopback packets are generated
* internally and always use the local CID as their source, including
* connections using a valid CID alias.
*
* The caller must hold the socket lock and must not call this for listening
* sockets, which accept packets from any source and have no assigned
* transport.
*/
bool vsock_check_source(const struct vsock_sock *vsk,
const struct vsock_transport *transport,
const struct sockaddr_vm *src)
{
if (vsk->transport != transport)
return false;
if (src->svm_port != vsk->remote_addr.svm_port)
return false;
if (src->svm_cid == vsk->remote_addr.svm_cid)
return true;
return transport->get_local_cid() == VMADDR_CID_LOCAL;
}
EXPORT_SYMBOL_GPL(vsock_check_source);
void vsock_remove_sock(struct vsock_sock *vsk)
{
/* Transport reassignment must not remove the binding. */

View File

@@ -1836,7 +1836,8 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
* lock_sock (note: listener sockets are not assigned to any transport)
*/
if (sock_flag(sk, SOCK_DONE) ||
(sk->sk_state != TCP_LISTEN && vsk->transport != &t->transport)) {
(sk->sk_state != TCP_LISTEN &&
!vsock_check_source(vsk, &t->transport, &src))) {
(void)virtio_transport_reset_no_sock(t, skb, net);
release_sock(sk);
sock_put(sk);

View File

@@ -680,11 +680,13 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
struct vmci_transport_packet *pkt;
struct vsock_sock *vsk;
bool bh_process_pkt;
bool drop_pkt;
int err;
sk = NULL;
err = VMCI_SUCCESS;
bh_process_pkt = false;
drop_pkt = false;
/* Ignore incoming packets from resources that aren't vsock
* implementations.
@@ -765,17 +767,29 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
bh_lock_sock(sk);
if (!sock_owned_by_user(sk)) {
/* The local context ID may be out of date, update it. */
vsk->local_addr.svm_cid = dst.svm_cid;
if (sk->sk_state != TCP_LISTEN &&
!vsock_check_source(vsk, &vmci_transport, &src)) {
drop_pkt = true;
err = VMCI_ERROR_NO_ACCESS;
} else {
/* The local context ID may be out of date, update it. */
vsk->local_addr.svm_cid = dst.svm_cid;
if (sk->sk_state == TCP_ESTABLISHED)
vmci_trans(vsk)->notify_ops->handle_notify_pkt(
sk, pkt, true, &dst, &src,
&bh_process_pkt);
if (sk->sk_state == TCP_ESTABLISHED)
vmci_trans(vsk)->notify_ops->handle_notify_pkt(sk, pkt, true,
&dst, &src,
&bh_process_pkt);
}
}
bh_unlock_sock(sk);
if (drop_pkt) {
if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
pr_err("unable to send reset\n");
goto out;
}
if (!bh_process_pkt) {
struct vmci_transport_recv_pkt_info *recv_pkt_info;
@@ -900,6 +914,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
{
struct vmci_transport_recv_pkt_info *recv_pkt_info;
struct vmci_transport_packet *pkt;
struct sockaddr_vm src;
struct sock *sk;
recv_pkt_info =
@@ -908,6 +923,12 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
pkt = &recv_pkt_info->pkt;
lock_sock(sk);
vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
if (sk->sk_state != TCP_LISTEN &&
!vsock_check_source(vsock_sk(sk), &vmci_transport, &src)) {
vmci_transport_reply_reset(pkt);
goto out;
}
/* The local context ID may be out of date. */
vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
@@ -937,6 +958,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
break;
}
out:
release_sock(sk);
kfree(recv_pkt_info);
/* Release reference obtained in the stream callback when we fetched

View File

@@ -28,6 +28,7 @@ TEST_PROGS := \
double_udp_encap.sh \
drop_monitor_tests.sh \
ecmp_rehash.sh \
exception_cache.sh \
fcnal-ipv4.sh \
fcnal-ipv6.sh \
fcnal-other.sh \

View File

@@ -0,0 +1,521 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test that the state of the route exception cache after an ICMP error is
# processed does not depend on whether the quoted packet was matched to a
# socket. Otherwise, an off-path attacker can probe the cache to discover the
# ephemeral port used by a connected UDP socket.
#
# When the quoted packet is not matched to a socket, the same exception is
# created as when it is matched, so that neither its presence nor its contents
# reveal the result of socket matching.
#
# +----+
# +---------| r1 |
# | +----+
# +----+ +--------+ | .1
# | h1 |---| bridge | | 198.51.100.0/30
# +----+ +--------+ | 2001:db8:2::/64
# .1 | | .2
# | +----+ +----+
# +---------| r2 |-----------| h2 |
# .2 .3 +----+ .1 .2 +----+
# 203.0.113.0/24
# 2001:db8:3::/64
# 192.0.2.0/24
# 2001:db8:1::/64
#
# Traffic from h1 to h2 is routed via r1, which reaches h2's network via r2
# over the point-to-point link. The MTU of the r2 - h2 link is lowered so that
# r2 emits ICMP errors towards h1.
#
# For the redirect tests r1's route to h2's network is replaced with one via r2
# on the shared segment, so that r1 forwards the packet back to the segment it
# arrived from and emits a redirect towards h1.
#
# The packets that provoke the ICMP errors are injected with a packet socket so
# that no socket is ever associated with them. A socket is created separately,
# with socat, when a test needs the ICMP error to be matched.
# shellcheck disable=SC1091,SC2034,SC2154,SC2329
source lib.sh
require_command jq
require_command mausezahn
require_command nstat
require_command socat
ALL_TESTS="
pmtu_no_socket_ipv4
pmtu_no_socket_ipv6
pmtu_socket_ipv4
pmtu_socket_ipv6
pmtu_omit_ipv4
pmtu_omit_ipv6
redirect_no_socket_ipv4
redirect_no_socket_ipv6
redirect_socket_ipv4
redirect_socket_ipv6
"
# Shared segment.
H1_ADDR4=192.0.2.1
R1_ADDR4=192.0.2.2
R2_ADDR4=192.0.2.3
H1_ADDR6=2001:db8:1::1
R1_ADDR6=2001:db8:1::2
R2_ADDR6=2001:db8:1::3
# r1 - r2 link.
R2_R1_ADDR4=198.51.100.2
R2_R1_ADDR6=2001:db8:2::2
# r2 - h2 link.
H2_ADDR4=203.0.113.2
H2_NET4=203.0.113.0/24
H2_ADDR6=2001:db8:3::2
H2_NET6=2001:db8:3::/64
SPORT=12345
DPORT=54321
# The MTU of the shared segment and of the r1 - r2 link. Large enough for the
# injected packets to reach r2 intact.
SEGMENT_MTU=2000
# Size of the injected packets. The PMTU tests need a size that exceeds every
# MTU used for the r2 - h2 link, so that r2 responds with an ICMP error. The
# redirect tests need a size that does not, otherwise r2 would respond with an
# ICMP error in addition to the redirect emitted by r1.
PMTU_PACKET_SIZE=1800
REDIRECT_PACKET_SIZE=100
# The MTUs used for the r2 - h2 link. All of them must be at least
# IPV6_MIN_MTU, otherwise IPv6 silently ignores the error instead of creating
# an exception.
MTU_MID=1400
MTU_LOW=1300
# Values for the IP{,V6}_MTU_DISCOVER socket option.
PMTUDISC_DONT=0
PMTUDISC_OMIT=5
SOCAT_PID=
linklocal_get()
{
local ns=$1; shift
local dev=$1; shift
ip -n "$ns" -j -6 addr show dev "$dev" | \
jq -r '.[]["addr_info"][] | select(.scope == "link") | .local'
}
linklocal_exists()
{
local ns=$1; shift
local dev=$1; shift
[ -n "$(linklocal_get "$ns" "$dev")" ]
}
family_vars_set()
{
local family=$1; shift
FAMILY=$family
if [ "$family" -eq 4 ]; then
H1_ADDR=$H1_ADDR4
H2_ADDR=$H2_ADDR4
MZ_FAMILY_OPT=()
# Without the Don't Fragment bit set r2 fragments the packet
# instead of reporting the MTU of the next hop.
MZ_IP_OPTS="df,"
SOCAT_DST="UDP4-CONNECT:$H2_ADDR4:$DPORT"
SOCAT_BIND="bind=$H1_ADDR4:$SPORT"
SOCAT_PMTUDISC="ip-mtu-discover"
else
H1_ADDR=$H1_ADDR6
H2_ADDR=$H2_ADDR6
MZ_FAMILY_OPT=(-6)
MZ_IP_OPTS=
SOCAT_DST="UDP6-CONNECT:[$H2_ADDR6]:$DPORT"
SOCAT_BIND="bind=[$H1_ADDR6]:$SPORT"
SOCAT_PMTUDISC="ipv6-mtu-discover"
fi
}
topology_setup()
{
local ns
setup_ns h1 r1 r2 h2 sw
defer cleanup_all_ns
# Link-local addresses are generated from the MAC address and read
# back during setup, so request that generation mode explicitly and
# make the addresses available as soon as the devices are brought up.
for ns in "$h1" "$r1" "$r2" "$h2" "$sw"; do
ip netns exec "$ns" sysctl -qw \
net.ipv6.conf.default.addr_gen_mode=0 \
net.ipv6.conf.default.accept_dad=0 \
net.ipv6.conf.all.accept_dad=0
done
ip -n "$sw" link add name br0 type bridge
ip -n "$sw" link set dev br0 mtu "$SEGMENT_MTU" up
ip -n "$h1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
peer name swp1 mtu "$SEGMENT_MTU" netns "$sw"
ip -n "$r1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
peer name swp2 mtu "$SEGMENT_MTU" netns "$sw"
ip -n "$r2" link add name eth0 mtu "$SEGMENT_MTU" type veth \
peer name swp3 mtu "$SEGMENT_MTU" netns "$sw"
ip -n "$r1" link add name eth1 mtu "$SEGMENT_MTU" type veth \
peer name eth1 mtu "$SEGMENT_MTU" netns "$r2"
ip -n "$r2" link add name eth2 type veth peer name eth0 netns "$h2"
ip -n "$sw" link set dev swp1 master br0 up
ip -n "$sw" link set dev swp2 master br0 up
ip -n "$sw" link set dev swp3 master br0 up
ip -n "$h1" link set dev eth0 up
ip -n "$r1" link set dev eth0 up
ip -n "$r1" link set dev eth1 up
ip -n "$r2" link set dev eth0 up
ip -n "$r2" link set dev eth1 up
ip -n "$r2" link set dev eth2 up
ip -n "$h2" link set dev eth0 up
ip -n "$h1" address add "$H1_ADDR4/24" dev eth0
ip -n "$r1" address add "$R1_ADDR4/24" dev eth0
ip -n "$r2" address add "$R2_ADDR4/24" dev eth0
ip -n "$r1" address add 198.51.100.1/30 dev eth1
ip -n "$r2" address add "$R2_R1_ADDR4/30" dev eth1
ip -n "$r2" address add 203.0.113.1/24 dev eth2
ip -n "$h2" address add "$H2_ADDR4/24" dev eth0
ip -n "$h1" -6 address add "$H1_ADDR6/64" dev eth0 nodad
ip -n "$r1" -6 address add "$R1_ADDR6/64" dev eth0 nodad
ip -n "$r2" -6 address add "$R2_ADDR6/64" dev eth0 nodad
ip -n "$r1" -6 address add 2001:db8:2::1/64 dev eth1 nodad
ip -n "$r2" -6 address add "$R2_R1_ADDR6/64" dev eth1 nodad
ip -n "$r2" -6 address add 2001:db8:3::1/64 dev eth2 nodad
ip -n "$h2" -6 address add "$H2_ADDR6/64" dev eth0 nodad
ip netns exec "$r1" sysctl -qw net.ipv4.ip_forward=1
ip netns exec "$r1" sysctl -qw net.ipv4.conf.all.send_redirects=1
ip netns exec "$r1" sysctl -qw net.ipv6.conf.all.forwarding=1
ip netns exec "$r2" sysctl -qw net.ipv4.ip_forward=1
ip netns exec "$r2" sysctl -qw net.ipv6.conf.all.forwarding=1
ip netns exec "$h1" sysctl -qw net.ipv4.conf.all.accept_redirects=1
ip netns exec "$h1" sysctl -qw net.ipv4.conf.eth0.accept_redirects=1
ip netns exec "$h1" sysctl -qw net.ipv6.conf.all.accept_redirects=1
ip netns exec "$h1" sysctl -qw net.ipv6.conf.eth0.accept_redirects=1
slowwait 5 linklocal_exists "$r1" eth0
check_err $? "r1: link-local address was not generated"
slowwait 5 linklocal_exists "$r2" eth0
check_err $? "r2: link-local address was not generated"
R1_LLADDR=$(linklocal_get "$r1" eth0)
R2_LLADDR=$(linklocal_get "$r2" eth0)
R1_MAC=$(ip -n "$r1" -j link show dev eth0 | jq -r '.[]["address"]')
R2_MAC=$(ip -n "$r2" -j link show dev eth0 | jq -r '.[]["address"]')
ip -n "$h1" route add "$H2_NET4" via "$R1_ADDR4" dev eth0
ip -n "$h1" -6 route add "$H2_NET6" via "$R1_LLADDR" dev eth0
ip -n "$r1" route add "$H2_NET4" via "$R2_R1_ADDR4" dev eth1
ip -n "$r1" -6 route add "$H2_NET6" via "$R2_R1_ADDR6" dev eth1
ip -n "$h2" route add default via 203.0.113.1 dev eth0
ip -n "$h2" -6 route add default via 2001:db8:3::1 dev eth0
far_mtu_set "$MTU_MID"
}
# Make r1 forward towards h2's network over the segment it receives the packet
# from, so that it emits a redirect towards h1.
redirect_route_set()
{
ip -n "$r1" route replace "$H2_NET4" via "$R2_ADDR4" dev eth0
ip -n "$r1" -6 route replace "$H2_NET6" via "$R2_LLADDR" dev eth0
# __ip_do_redirect() only creates an exception if the new gateway is
# already a valid neighbour. Otherwise it merely triggers address
# resolution. IPv6 resolves the target itself, in rt6_do_redirect().
ip -n "$h1" neigh replace "$R2_ADDR4" lladdr "$R2_MAC" dev eth0 \
nud permanent
}
far_mtu_set()
{
local mtu=$1; shift
ip -n "$r2" link set dev eth2 mtu "$mtu"
ip -n "$h2" link set dev eth0 mtu "$mtu"
}
socket_is_open()
{
ip netns exec "$h1" ss -uHn "sport = :$SPORT" | grep -q .
}
socket_start()
{
# Disable PMTU discovery by default so that ICMP errors are not
# reported to the socket. Otherwise socat would exit when the first one
# arrives and later packets in the same test would not be matched to a
# socket. The exception is still created, as ip{,6}_sk_accept_pmtu()
# only rejects IP{,V6}_PMTUDISC_{INTERFACE,OMIT}.
local pmtudisc=${1:-$PMTUDISC_DONT}
# Send socat's diagnostics to /dev/null. It reports the ICMP errors
# that reach the socket, which is exactly what the tests provoke.
ip netns exec "$h1" socat -u -lf/dev/null \
"$SOCAT_DST,$SOCAT_BIND,$SOCAT_PMTUDISC=$pmtudisc" \
OPEN:/dev/null,wronly=1 &
SOCAT_PID=$!
defer socket_stop
slowwait 5 socket_is_open
check_err $? "socket did not open"
}
socket_stop()
{
[ -z "$SOCAT_PID" ] && return 0
kill "$SOCAT_PID" &> /dev/null
wait "$SOCAT_PID" 2> /dev/null
SOCAT_PID=
}
# Inject a packet towards h2 with a packet socket. No socket is associated with
# it, so an ICMP error quoting it is matched to a socket only if one was
# created separately with the same source port.
packet_send()
{
local size=$1; shift
ip netns exec "$h1" mausezahn "${MZ_FAMILY_OPT[@]}" eth0 \
-a own -b "$R1_MAC" -A "$H1_ADDR" -B "$H2_ADDR" \
-t udp "${MZ_IP_OPTS}sp=$SPORT,dp=$DPORT" \
-p "$size" -c 1 -q
}
exception_show()
{
if [ "$FAMILY" -eq 4 ]; then
# IPv4 exceptions without a bound route are not dumped, but
# "route get" reports the exception and binds a route to it.
ip -n "$h1" route get "$H2_ADDR"
else
# IPv6 does not report a cache indication in "route get"
# output, so dump the exceptions instead.
ip -n "$h1" -6 route show cache | grep -F "$H2_ADDR" || true
fi
}
exception_mtu_get()
{
exception_show | grep -o "mtu [0-9]*" | cut -d ' ' -f 2
}
exception_gw_get()
{
exception_show | grep -o "via [0-9a-f.:]*" | cut -d ' ' -f 2
}
exception_mtu_check()
{
local expected=$1; shift
[ "$(exception_mtu_get)" = "$expected" ]
}
icmp_errors_get()
{
local ctr=IcmpInDestUnreachs
[ "$FAMILY" -eq 6 ] && ctr=Icmp6InPktTooBigs
ip netns exec "$h1" nstat -asz "$ctr" | \
awk -v ctr="$ctr" '$1 == ctr { print $2 }'
}
exception_pmtu_check()
{
local mtu=$1; shift
local desc=$1; shift
busywait "$BUSYWAIT_TIMEOUT" exception_mtu_check "$mtu"
check_err $? "$desc: exception does not carry an MTU of $mtu"
}
pmtu_no_socket()
{
local family=$1; shift
RET=0
family_vars_set "$family"
topology_setup
packet_send "$PMTU_PACKET_SIZE"
exception_pmtu_check "$MTU_MID" "No socket"
log_test "IPv$family: PMTU: exception without a matching socket"
}
pmtu_no_socket_ipv4()
{
pmtu_no_socket 4
}
pmtu_no_socket_ipv6()
{
pmtu_no_socket 6
}
pmtu_socket()
{
local family=$1; shift
local t0
RET=0
family_vars_set "$family"
topology_setup
socket_start
packet_send "$PMTU_PACKET_SIZE"
exception_pmtu_check "$MTU_MID" "Matching socket"
# A lower PMTU replaces the one currently stored in the exception.
far_mtu_set "$MTU_LOW"
packet_send "$PMTU_PACKET_SIZE"
exception_pmtu_check "$MTU_LOW" "Lower PMTU"
# A higher PMTU is ignored, so the exception is left as it is. Wait
# for the error to be received, as otherwise the check below would
# pass even if it never was.
far_mtu_set "$MTU_MID"
t0=$(icmp_errors_get)
packet_send "$PMTU_PACKET_SIZE"
busywait "$BUSYWAIT_TIMEOUT" until_counter_is ">= $((t0 + 1))" \
icmp_errors_get > /dev/null
check_err $? "Higher PMTU: ICMP error was not received"
exception_mtu_check "$MTU_LOW"
check_err $? "Higher PMTU: exception does not carry an MTU of $MTU_LOW"
log_test "IPv$family: PMTU: exception with a matching socket"
}
pmtu_socket_ipv4()
{
pmtu_socket 4
}
pmtu_socket_ipv6()
{
pmtu_socket 6
}
pmtu_omit()
{
local family=$1; shift
RET=0
family_vars_set "$family"
topology_setup
socket_start "$PMTUDISC_OMIT"
packet_send "$PMTU_PACKET_SIZE"
exception_pmtu_check "$MTU_MID" "PMTU discovery disabled"
log_test "IPv$family: PMTU: exception with a socket ignoring it"
}
pmtu_omit_ipv4()
{
pmtu_omit 4
}
pmtu_omit_ipv6()
{
pmtu_omit 6
}
exception_gw_check()
{
local expected=$1; shift
[ -n "$expected" ] && [ "$(exception_gw_get)" = "$expected" ]
}
redirect_gw_new()
{
if [ "$FAMILY" -eq 4 ]; then
echo "$R2_ADDR4"
else
echo "$R2_LLADDR"
fi
}
redirect_no_socket()
{
local family=$1; shift
RET=0
family_vars_set "$family"
topology_setup
redirect_route_set
packet_send "$REDIRECT_PACKET_SIZE"
busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
check_err $? "No socket: exception does not carry the new gateway"
log_test "IPv$family: Redirect: exception without a matching socket"
}
redirect_no_socket_ipv4()
{
redirect_no_socket 4
}
redirect_no_socket_ipv6()
{
redirect_no_socket 6
}
redirect_socket()
{
local family=$1; shift
RET=0
family_vars_set "$family"
topology_setup
redirect_route_set
socket_start
packet_send "$REDIRECT_PACKET_SIZE"
busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
check_err $? "Matching socket: exception does not carry the new gateway"
log_test "IPv$family: Redirect: exception with a matching socket"
}
redirect_socket_ipv4()
{
redirect_socket 4
}
redirect_socket_ipv6()
{
redirect_socket 6
}
trap defer_scopes_cleanup EXIT
tests_run
exit "$EXIT_STATUS"

View File

@@ -685,6 +685,9 @@ star_g_common()
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010"
log_test $? 255 "Invalid source in source list"
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010"
log_test $? 255 "All-zeros source in source list"
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010"
log_test $? 255 "Source list without filter mode"
}
@@ -784,6 +787,9 @@ sg_common()
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010"
log_test $? 255 "(S, G) with an invalid source list"
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010"
log_test $? 255 "(S, G) with an all-zeros source"
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010"
log_test $? 255 "All-zeros group with source"
}

View File

@@ -353,5 +353,28 @@
"teardown": [
"$TC qdisc del dev $DEV1 parent root drr"
]
},
{
"id": "70fd",
"name": "Add u32 filter when node ID pool is exhausted (4096th filter rejected)",
"category": [
"filter",
"u32"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
"$TC qdisc add dev $DUMMY clsact",
"bash -c 'for i in {1..4095}; do echo filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0; done | $TC -b -'"
],
"cmdUnderTest": "$TC filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0",
"expExitCode": "2",
"verifyCmd": "$TC -d filter show dev $DUMMY ingress",
"matchPattern": "fh 800::",
"matchCount": "4095",
"teardown": [
"$TC qdisc del dev $DUMMY clsact"
]
}
]