mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
Including fixes from netfilter.
Current release - regressions:
- netfilter: walk fib6_siblings under RCU
Previous releases - regressions:
- netlink: fix sending unassigned nsid after assigned one
- bridge: fix sleep in atomic context in netlink path
- sched: fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
- ipv4: fix net->ipv4.sysctl_local_reserved_ports UaF
- eth: tun: free page on short-frame rejection in tun_xdp_one()
Previous releases - always broken:
- skbuff: fix missing zerocopy reference in pskb_carve helpers
- handshake: drain pending requests at net namespace exit
- ethtool:
- rss: avoid modifying the RSS context response
- module: avoid leaking a netdev ref on module flash errors
- coalesce: cap profile updates at NET_DIM_PARAMS_NUM_PROFILES
- netfilter: fix dst corruption in same register operation
- nfc: hci: fix out-of-bounds read in HCP header parsing
- ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo()
- eth: vti: use ip6_tnl.net in vti6_changelink().
- eth: vxlan: do not reuse cached ip_hdr() value after skb_tunnel_check_pmtu()
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-----BEGIN PGP SIGNATURE-----
iQJGBAABCgAwFiEEg1AjqC77wbdLX2LbKSR5jcyPE6QFAmoYVTISHHBhYmVuaUBy
ZWRoYXQuY29tAAoJECkkeY3MjxOkYfoP/jBxdUf2IirOjl/vjJFm7cXzcCdTWreb
HmlvVRPF0YDuwQEjaZA+Ed/+wi0QIiyckI60Ltpfz9DbSm3ugstfUxPNWKVb5HZQ
TI1diAa+uTmaXndC5Kb56U/KNMcMZOJ0FZwHheU2mC/7USpB9S/gaGYf2vxCOF9B
huMrCuvoHhASxaL6W1xyYR3P4ouGS9XoQU/sGRWAynpi45BZdFF/Y8W2YrCk0IKc
SwkWbId2Ek6/2+f3pWKYbE88UEjpNh2U6K+kcAgy/UN3N0+tb91kuOrn/5Z+WjE7
3ZdEBvALj6K0P7BxsR64M1ikVgm2KcZAn8UH5UOqkzlP3VGWHYbbk/4KvEGD1oJF
p0lauztIkPPdq16Dau8v+KHw5UU4vBpEDo3323hh7kcSIu7cJkWSVxo7/WDjokzT
HlIZtzKpXwCUSSCNmV3y3zXR/Xl41HOzU5lZv6f8P2hkMfyIu9te9lXF6Foc6r2u
Ng0oVkevURpGhqpKQKxRtaApPrfOCYFkN4aVzvm5haxhFcughJZmQcjVbu03l4CM
/nddhYop7D2NdnZzSdlBO1bK/KBebZCYlSKZJGjdL7zqIOQAjjw9UoW0rU+84pkU
dcvFBPm+iWAhvwWEGaUrnuNcYth/umNMTzC4domLUyPrVydSUH0zi0RQYc9mXffR
EvWEj952b4o0
=IBwj
-----END PGP SIGNATURE-----
Merge tag 'net-7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Paolo Abeni:
"This is again significantly bigger than the same point into the
previous cycle, but at least smaller than last week.
I'm not aware of any pending regression for the current cycle.
Including fixes from netfilter.
Current release - regressions:
- netfilter: walk fib6_siblings under RCU
Previous releases - regressions:
- netlink: fix sending unassigned nsid after assigned one
- bridge: fix sleep in atomic context in netlink path
- sched: fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
- ipv4: fix net->ipv4.sysctl_local_reserved_ports UaF
- eth: tun: free page on short-frame rejection in tun_xdp_one()
Previous releases - always broken:
- skbuff: fix missing zerocopy reference in pskb_carve helpers
- handshake: drain pending requests at net namespace exit
- ethtool:
- rss: avoid modifying the RSS context response
- module: avoid leaking a netdev ref on module flash errors
- coalesce: cap profile updates at NET_DIM_PARAMS_NUM_PROFILES
- netfilter: fix dst corruption in same register operation
- nfc: hci: fix out-of-bounds read in HCP header parsing
- ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo()
- eth:
- vti: use ip6_tnl.net in vti6_changelink().
- vxlan: do not reuse cached ip_hdr() value after
skb_tunnel_check_pmtu()"
* tag 'net-7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (94 commits)
dpll: zl3073x: make frequency monitor a per-device attribute
dpll: zl3073x: use __dpll_device_change_ntf() and remove change_work
dpll: export __dpll_device_change_ntf() for use under dpll_lock
net/handshake: Drain pending requests at net namespace exit
net/handshake: Verify file-reference balance in submit paths
net/handshake: Close the submit-side sock_hold race
net/handshake: hand off the pinned file reference to accept_doit
net/handshake: Take a long-lived file reference at submit
net/handshake: Pass negative errno through handshake_complete()
nvme-tcp: store negative errno in queue->tls_err
net/handshake: Use spin_lock_bh for hn_lock
net: skbuff: fix missing zerocopy reference in pskb_carve helpers
net: hibmcge: move dma_rmb() after dma_sync_single_for_cpu() in RX path
net: hibmcge: disable Relaxed Ordering to fix RX packet corruption
selftests/tc-testing: Add netem test case exercising loops
selftests/tc-testing: Add mirred test cases exercising loops
net/sched: act_mirred: Fix return code in early mirred redirect error paths
net/sched: act_mirred: Fix blockcast recursion bypass leading to stack overflow
net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
net/sched: fix packet loop on netem when duplicate is on
...
This commit is contained in:
commit
3e20009988
|
|
@ -12,6 +12,12 @@ protocol: genetlink
|
|||
doc: Netlink protocol to request a transport layer security handshake.
|
||||
|
||||
definitions:
|
||||
-
|
||||
type: const
|
||||
name: max-errno
|
||||
value: 4095
|
||||
header: linux/err.h
|
||||
scope: kernel
|
||||
-
|
||||
type: enum
|
||||
name: handler-class
|
||||
|
|
@ -80,6 +86,8 @@ attribute-sets:
|
|||
-
|
||||
name: status
|
||||
type: u32
|
||||
checks:
|
||||
max: max-errno
|
||||
-
|
||||
name: sockfd
|
||||
type: s32
|
||||
|
|
|
|||
|
|
@ -829,12 +829,21 @@ int dpll_device_delete_ntf(struct dpll_device *dpll)
|
|||
return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
|
||||
}
|
||||
|
||||
static int
|
||||
__dpll_device_change_ntf(struct dpll_device *dpll)
|
||||
/**
|
||||
* __dpll_device_change_ntf - notify that the dpll device has been changed
|
||||
* @dpll: registered dpll pointer
|
||||
*
|
||||
* Context: caller must hold dpll_lock. Suitable for use inside device
|
||||
* callbacks which are already invoked under dpll_lock.
|
||||
* Return: 0 if succeeds, error code otherwise.
|
||||
*/
|
||||
int __dpll_device_change_ntf(struct dpll_device *dpll)
|
||||
{
|
||||
lockdep_assert_held(&dpll_lock);
|
||||
dpll_device_notify(dpll, DPLL_DEVICE_CHANGED);
|
||||
return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__dpll_device_change_ntf);
|
||||
|
||||
/**
|
||||
* dpll_device_change_ntf - notify that the dpll device has been changed
|
||||
|
|
|
|||
|
|
@ -762,18 +762,15 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
|||
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
|
||||
/* Update measured input reference frequencies if any DPLL has
|
||||
* frequency monitoring enabled.
|
||||
/* Update measured input reference frequencies if frequency
|
||||
* monitoring is enabled.
|
||||
*/
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list) {
|
||||
if (zldpll->freq_monitor) {
|
||||
rc = zl3073x_ref_freq_meas_update(zldev);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to update measured frequencies: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
break;
|
||||
}
|
||||
if (zldev->freq_monitor) {
|
||||
rc = zl3073x_ref_freq_meas_update(zldev);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to update measured frequencies: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
}
|
||||
|
||||
/* Update references' fractional frequency offsets */
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ struct zl3073x_chip_info {
|
|||
* @work: periodic work
|
||||
* @clock_id: clock id of the device
|
||||
* @phase_avg_factor: phase offset measurement averaging factor
|
||||
* @freq_monitor: is frequency monitor enabled
|
||||
*/
|
||||
struct zl3073x_dev {
|
||||
struct device *dev;
|
||||
|
|
@ -77,9 +78,10 @@ struct zl3073x_dev {
|
|||
struct kthread_worker *kworker;
|
||||
struct kthread_delayed_work work;
|
||||
|
||||
/* Devlink parameters */
|
||||
/* Per-chip parameters */
|
||||
u64 clock_id;
|
||||
u8 phase_avg_factor;
|
||||
bool freq_monitor;
|
||||
};
|
||||
|
||||
extern const struct regmap_config zl3073x_regmap_config;
|
||||
|
|
|
|||
|
|
@ -1079,15 +1079,6 @@ zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dpll_change_work(struct work_struct *work)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll;
|
||||
|
||||
zldpll = container_of(work, struct zl3073x_dpll, change_work);
|
||||
dpll_device_change_ntf(zldpll->dpll_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 factor,
|
||||
|
|
@ -1113,8 +1104,10 @@ zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
|
|||
* we have to send a notification for other DPLL devices.
|
||||
*/
|
||||
list_for_each_entry(item, &zldpll->dev->dplls, list) {
|
||||
if (item != zldpll)
|
||||
schedule_work(&item->change_work);
|
||||
struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev);
|
||||
|
||||
if (item != zldpll && dpll_dev)
|
||||
__dpll_device_change_ntf(dpll_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1219,7 +1212,7 @@ zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll,
|
|||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
|
||||
if (zldpll->freq_monitor)
|
||||
if (zldpll->dev->freq_monitor)
|
||||
*state = DPLL_FEATURE_STATE_ENABLE;
|
||||
else
|
||||
*state = DPLL_FEATURE_STATE_DISABLE;
|
||||
|
|
@ -1233,9 +1226,19 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
|||
enum dpll_feature_state state,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dpll *item, *zldpll = dpll_priv;
|
||||
|
||||
zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
|
||||
zldpll->dev->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
|
||||
|
||||
/* The frequency monitoring is common for all DPLL channels so after
|
||||
* change we have to send a notification for other DPLL devices.
|
||||
*/
|
||||
list_for_each_entry(item, &zldpll->dev->dplls, list) {
|
||||
struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev);
|
||||
|
||||
if (item != zldpll && dpll_dev)
|
||||
__dpll_device_change_ntf(dpll_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1627,13 +1630,13 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
|||
static void
|
||||
zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
|
||||
{
|
||||
WARN(!zldpll->dpll_dev, "DPLL device is not registered\n");
|
||||
struct dpll_device *dpll_dev = READ_ONCE(zldpll->dpll_dev);
|
||||
|
||||
cancel_work_sync(&zldpll->change_work);
|
||||
WARN(!dpll_dev, "DPLL device is not registered\n");
|
||||
|
||||
dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll);
|
||||
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
||||
zldpll->dpll_dev = NULL;
|
||||
WRITE_ONCE(zldpll->dpll_dev, NULL);
|
||||
dpll_device_unregister(dpll_dev, &zldpll->ops, zldpll);
|
||||
dpll_device_put(dpll_dev, &zldpll->tracker);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1752,7 +1755,7 @@ zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin)
|
|||
u8 ref_id;
|
||||
u32 freq;
|
||||
|
||||
if (!zldpll->freq_monitor)
|
||||
if (!zldpll->dev->freq_monitor)
|
||||
return false;
|
||||
|
||||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
|
|
@ -1785,10 +1788,8 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
|||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct device *dev = zldev->dev;
|
||||
const struct zl3073x_chan *chan;
|
||||
struct zl3073x_dpll_pin *pin;
|
||||
int rc;
|
||||
u8 mode;
|
||||
|
||||
zldpll->check_count++;
|
||||
|
||||
|
|
@ -1807,15 +1808,6 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
|||
dpll_device_change_ntf(zldpll->dpll_dev);
|
||||
}
|
||||
|
||||
/* Input pin monitoring does make sense only in automatic
|
||||
* or forced reference modes.
|
||||
*/
|
||||
chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
mode = zl3073x_chan_mode_get(chan);
|
||||
if (mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
|
||||
return;
|
||||
|
||||
/* Update phase offset latch registers for this DPLL if the phase
|
||||
* offset monitor feature is enabled.
|
||||
*/
|
||||
|
|
@ -1926,7 +1918,6 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
|
|||
zldpll->dev = zldev;
|
||||
zldpll->id = ch;
|
||||
INIT_LIST_HEAD(&zldpll->pins);
|
||||
INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work);
|
||||
|
||||
return zldpll;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,11 @@
|
|||
* @id: DPLL index
|
||||
* @check_count: periodic check counter
|
||||
* @phase_monitor: is phase offset monitor enabled
|
||||
* @freq_monitor: is frequency monitor enabled
|
||||
* @ops: DPLL device operations for this instance
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @tracker: tracking object for the acquired reference
|
||||
* @lock_status: last saved DPLL lock status
|
||||
* @pins: list of pins
|
||||
* @change_work: device change notification work
|
||||
*/
|
||||
struct zl3073x_dpll {
|
||||
struct list_head list;
|
||||
|
|
@ -29,13 +27,11 @@ struct zl3073x_dpll {
|
|||
u8 id;
|
||||
u8 check_count;
|
||||
bool phase_monitor;
|
||||
bool freq_monitor;
|
||||
struct dpll_device_ops ops;
|
||||
struct dpll_device *dpll_dev;
|
||||
dpll_tracker tracker;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct list_head pins;
|
||||
struct work_struct change_work;
|
||||
};
|
||||
|
||||
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
|
||||
|
|
|
|||
|
|
@ -1890,6 +1890,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
|
|||
struct sockaddr_storage ss;
|
||||
int res = 0, i;
|
||||
|
||||
if (slave_dev->type == ARPHRD_CAN) {
|
||||
BOND_NL_ERR(bond_dev, extack,
|
||||
"CAN devices cannot be enslaved");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (slave_dev->flags & IFF_MASTER &&
|
||||
!netif_is_bond_master(slave_dev)) {
|
||||
BOND_NL_ERR(bond_dev, extack,
|
||||
|
|
|
|||
|
|
@ -420,6 +420,9 @@ static int hbg_pci_init(struct pci_dev *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_RELAX_EN);
|
||||
pci_save_state(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -452,12 +452,12 @@ static bool hbg_sync_data_from_hw(struct hbg_priv *priv,
|
|||
{
|
||||
struct hbg_rx_desc *rx_desc;
|
||||
|
||||
/* make sure HW write desc complete */
|
||||
dma_rmb();
|
||||
|
||||
dma_sync_single_for_cpu(&priv->pdev->dev, buffer->page_dma,
|
||||
buffer->page_size, DMA_FROM_DEVICE);
|
||||
|
||||
/* make sure HW write desc complete */
|
||||
dma_rmb();
|
||||
|
||||
rx_desc = (struct hbg_rx_desc *)buffer->page_addr;
|
||||
return FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2) != 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc)
|
|||
return &rvu->pf[rvu_get_pf(rvu->pdev, pcifunc)];
|
||||
}
|
||||
|
||||
static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
|
||||
bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
|
||||
{
|
||||
int pf, vf, nvfs;
|
||||
u64 cfg;
|
||||
|
|
|
|||
|
|
@ -917,6 +917,7 @@ u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkaddr);
|
|||
struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc);
|
||||
void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf);
|
||||
bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr);
|
||||
bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc);
|
||||
bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype);
|
||||
int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot);
|
||||
int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,14 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,
|
|||
{
|
||||
struct rep_evtq_ent *qentry;
|
||||
|
||||
/* The mailbox dispatcher normalises only the header pcifunc; the
|
||||
* nested struct rep_event::pcifunc body field is sender-controlled
|
||||
* and is later used by rvu_rep_up_notify() to index rvu->pf[] /
|
||||
* rvu->hwvf[]. Reject out-of-range body selectors before queueing.
|
||||
*/
|
||||
if (!is_pf_func_valid(rvu, req->pcifunc))
|
||||
return -EINVAL;
|
||||
|
||||
qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
|
||||
if (!qentry)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -1320,8 +1320,10 @@ mlx5_cmd_hws_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
|
|||
break;
|
||||
case MLX5_REFORMAT_TYPE_REMOVE_HDR:
|
||||
hws_action = mlx5_fs_get_action_remove_header_vlan(fs_ctx, params);
|
||||
if (!hws_action)
|
||||
if (!hws_action) {
|
||||
mlx5_core_err(dev, "Only vlan remove header supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n",
|
||||
|
|
|
|||
|
|
@ -808,7 +808,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u
|
|||
if (pn + 1 > rx_sa->next_pn_halves.lower) {
|
||||
rx_sa->next_pn_halves.lower = pn + 1;
|
||||
} else if (secy->xpn &&
|
||||
!pn_same_half(pn, rx_sa->next_pn_halves.lower)) {
|
||||
(pn + 1 == 0 ||
|
||||
!pn_same_half(pn, rx_sa->next_pn_halves.lower))) {
|
||||
rx_sa->next_pn_halves.upper++;
|
||||
rx_sa->next_pn_halves.lower = pn + 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy-common-props.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/wordpart.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
|
@ -170,9 +171,23 @@
|
|||
#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13)
|
||||
#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14)
|
||||
|
||||
#define AN8811HB_MCU_SW_RST 0x5cf9f8
|
||||
#define AN8811HB_MCU_SW_RST_HOLD BIT(16)
|
||||
#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0))
|
||||
#define AN8811HB_MCU_SW_START 0x5cf9fc
|
||||
#define AN8811HB_MCU_SW_START_EN BIT(16)
|
||||
|
||||
/* MII register constants for PBUS access (PHY addr + 8) */
|
||||
#define AIR_PBUS_ADDR_HIGH 0x1c
|
||||
#define AIR_PBUS_DATA_HIGH 0x10
|
||||
#define AIR_PBUS_REG_ADDR_HIGH_MASK GENMASK(15, 6)
|
||||
#define AIR_PBUS_REG_ADDR_LOW_MASK GENMASK(5, 2)
|
||||
|
||||
/* Led definitions */
|
||||
#define EN8811H_LED_COUNT 3
|
||||
|
||||
#define EN8811H_PBUS_ADDR_OFFS 8
|
||||
|
||||
/* Default LED setup:
|
||||
* GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
|
||||
* GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps
|
||||
|
|
@ -201,6 +216,7 @@ struct en8811h_priv {
|
|||
struct clk_hw hw;
|
||||
struct phy_device *phydev;
|
||||
unsigned int cko_is_enabled;
|
||||
struct mdio_device *pbusdev;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -254,6 +270,31 @@ static int air_phy_write_page(struct phy_device *phydev, int page)
|
|||
return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
|
||||
}
|
||||
|
||||
static int __air_pbus_reg_write(struct mdio_device *mdiodev,
|
||||
u32 pbus_reg, u32 pbus_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_EXT_PAGE_ACCESS,
|
||||
upper_16_bits(pbus_reg));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_ADDR_HIGH,
|
||||
FIELD_GET(AIR_PBUS_REG_ADDR_HIGH_MASK, pbus_reg));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = __mdiobus_write(mdiodev->bus, mdiodev->addr,
|
||||
FIELD_GET(AIR_PBUS_REG_ADDR_LOW_MASK, pbus_reg),
|
||||
lower_16_bits(pbus_data));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_DATA_HIGH,
|
||||
upper_16_bits(pbus_data));
|
||||
}
|
||||
|
||||
static int __air_buckpbus_reg_write(struct phy_device *phydev,
|
||||
u32 pbus_address, u32 pbus_data)
|
||||
{
|
||||
|
|
@ -570,10 +611,67 @@ static int an8811hb_load_file(struct phy_device *phydev, const char *name,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int an8811hb_mcu_assert(struct phy_device *phydev)
|
||||
{
|
||||
struct en8811h_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
phy_lock_mdio_bus(phydev);
|
||||
|
||||
ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST,
|
||||
AN8811HB_MCU_SW_RST_HOLD);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START, 0);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
msleep(50);
|
||||
phydev_dbg(phydev, "MCU asserted\n");
|
||||
|
||||
unlock:
|
||||
phy_unlock_mdio_bus(phydev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int an8811hb_mcu_deassert(struct phy_device *phydev)
|
||||
{
|
||||
struct en8811h_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
phy_lock_mdio_bus(phydev);
|
||||
|
||||
ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START,
|
||||
AN8811HB_MCU_SW_START_EN);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST,
|
||||
AN8811HB_MCU_SW_RST_RUN);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
msleep(50);
|
||||
phydev_dbg(phydev, "MCU deasserted\n");
|
||||
|
||||
unlock:
|
||||
phy_unlock_mdio_bus(phydev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int an8811hb_load_firmware(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = an8811hb_mcu_assert(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = an8811hb_mcu_deassert(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
||||
EN8811H_FW_CTRL_1_START);
|
||||
if (ret < 0)
|
||||
|
|
@ -662,6 +760,16 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (phy_id_compare_model(phydev->phy_id, AN8811HB_PHY_ID)) {
|
||||
ret = an8811hb_mcu_assert(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = an8811hb_mcu_deassert(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
||||
EN8811H_FW_CTRL_1_START);
|
||||
if (ret < 0)
|
||||
|
|
@ -1166,6 +1274,7 @@ static int en8811h_leds_setup(struct phy_device *phydev)
|
|||
|
||||
static int an8811hb_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct mdio_device *mdiodev;
|
||||
struct en8811h_priv *priv;
|
||||
int ret;
|
||||
|
||||
|
|
@ -1175,10 +1284,28 @@ static int an8811hb_probe(struct phy_device *phydev)
|
|||
return -ENOMEM;
|
||||
phydev->priv = priv;
|
||||
|
||||
/*
|
||||
* The AN8811HB PHY address is restricted to 8-15 (decimal),
|
||||
* depending on the board hardware strapping.
|
||||
* This means the PBUS address is only in the range 16-21 (decimal),
|
||||
* so we do not need to handle the case
|
||||
* where the PBUS address exceeds 31 (decimal).
|
||||
*/
|
||||
mdiodev = mdio_device_create(phydev->mdio.bus,
|
||||
phydev->mdio.addr + EN8811H_PBUS_ADDR_OFFS);
|
||||
if (IS_ERR(mdiodev))
|
||||
return PTR_ERR(mdiodev);
|
||||
|
||||
ret = mdio_device_register(mdiodev);
|
||||
if (ret)
|
||||
goto err_dev_free;
|
||||
|
||||
priv->pbusdev = mdiodev;
|
||||
|
||||
ret = an8811hb_load_firmware(phydev);
|
||||
if (ret < 0) {
|
||||
phydev_err(phydev, "Load firmware failed: %d\n", ret);
|
||||
return ret;
|
||||
goto err_dev_create;
|
||||
}
|
||||
|
||||
en8811h_print_fw_version(phydev);
|
||||
|
|
@ -1191,22 +1318,29 @@ static int an8811hb_probe(struct phy_device *phydev)
|
|||
|
||||
ret = en8811h_leds_setup(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_dev_create;
|
||||
|
||||
priv->phydev = phydev;
|
||||
/* Co-Clock Output */
|
||||
ret = an8811hb_clk_provider_setup(&phydev->mdio.dev, &priv->hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_dev_create;
|
||||
|
||||
/* Configure led gpio pins as output */
|
||||
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
|
||||
AN8811HB_GPIO_OUTPUT_345,
|
||||
AN8811HB_GPIO_OUTPUT_345);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_dev_create;
|
||||
|
||||
return 0;
|
||||
|
||||
err_dev_create:
|
||||
mdio_device_remove(mdiodev);
|
||||
|
||||
err_dev_free:
|
||||
mdio_device_free(mdiodev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int en8811h_probe(struct phy_device *phydev)
|
||||
|
|
@ -1561,6 +1695,16 @@ static int en8811h_suspend(struct phy_device *phydev)
|
|||
return genphy_suspend(phydev);
|
||||
}
|
||||
|
||||
static void an8811hb_remove(struct phy_device *phydev)
|
||||
{
|
||||
struct en8811h_priv *priv = phydev->priv;
|
||||
|
||||
if (priv->pbusdev) {
|
||||
mdio_device_remove(priv->pbusdev);
|
||||
mdio_device_free(priv->pbusdev);
|
||||
}
|
||||
}
|
||||
|
||||
static struct phy_driver en8811h_driver[] = {
|
||||
{
|
||||
PHY_ID_MATCH_MODEL(EN8811H_PHY_ID),
|
||||
|
|
@ -1587,6 +1731,7 @@ static struct phy_driver en8811h_driver[] = {
|
|||
PHY_ID_MATCH_MODEL(AN8811HB_PHY_ID),
|
||||
.name = "Airoha AN8811HB",
|
||||
.probe = an8811hb_probe,
|
||||
.remove = an8811hb_remove,
|
||||
.get_features = en8811h_get_features,
|
||||
.config_init = an8811hb_config_init,
|
||||
.get_rate_matching = en8811h_get_rate_matching,
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
|
|||
int err, depth;
|
||||
|
||||
if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
|
||||
put_page(virt_to_head_page(xdp->data));
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
|
|||
|
||||
skb = build_skb(xdp->data_hard_start, buflen);
|
||||
if (!skb) {
|
||||
put_page(virt_to_head_page(xdp->data));
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,21 +534,23 @@ static void team_adjust_ops(struct team *team)
|
|||
|
||||
if (!team->tx_en_port_count || !team_is_mode_set(team) ||
|
||||
!team->mode->ops->transmit)
|
||||
team->ops.transmit = team_dummy_transmit;
|
||||
WRITE_ONCE(team->ops.transmit, team_dummy_transmit);
|
||||
else
|
||||
team->ops.transmit = team->mode->ops->transmit;
|
||||
WRITE_ONCE(team->ops.transmit, team->mode->ops->transmit);
|
||||
|
||||
if (!team->rx_en_port_count || !team_is_mode_set(team) ||
|
||||
!team->mode->ops->receive)
|
||||
team->ops.receive = team_dummy_receive;
|
||||
WRITE_ONCE(team->ops.receive, team_dummy_receive);
|
||||
else
|
||||
team->ops.receive = team->mode->ops->receive;
|
||||
WRITE_ONCE(team->ops.receive, team->mode->ops->receive);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can benefit from the fact that it's ensured no port is present
|
||||
* at the time of mode change. Therefore no packets are in fly so there's no
|
||||
* need to set mode operations in any special way.
|
||||
* team_change_mode() ensures no ports are present during mode change,
|
||||
* but lockless readers can still reach team_xmit(). Avoid touching
|
||||
* transmit/receive -- they are already set to dummies by
|
||||
* team_adjust_ops() since no ports are enabled. synchronize_net()
|
||||
* drains in-flight readers before destroying old mode state.
|
||||
*/
|
||||
static int __team_change_mode(struct team *team,
|
||||
const struct team_mode *new_mode)
|
||||
|
|
@ -557,9 +559,21 @@ static int __team_change_mode(struct team *team,
|
|||
if (team_is_mode_set(team)) {
|
||||
void (*exit_op)(struct team *team) = team->ops.exit;
|
||||
|
||||
/* Clear ops area so no callback is called any longer */
|
||||
memset(&team->ops, 0, sizeof(struct team_mode_ops));
|
||||
team_adjust_ops(team);
|
||||
/* Clear cold-path ops used only under RTNL. transmit and
|
||||
* receive are already dummies (no ports) so leave them
|
||||
* alone -- overwriting them is the source of the race.
|
||||
*/
|
||||
team->ops.init = NULL;
|
||||
team->ops.exit = NULL;
|
||||
team->ops.port_enter = NULL;
|
||||
team->ops.port_leave = NULL;
|
||||
team->ops.port_change_dev_addr = NULL;
|
||||
team->ops.port_tx_disabled = NULL;
|
||||
|
||||
/* Wait for in-flight readers before tearing down mode
|
||||
* state they may reference.
|
||||
*/
|
||||
synchronize_net();
|
||||
|
||||
if (exit_op)
|
||||
exit_op(team);
|
||||
|
|
@ -582,7 +596,12 @@ static int __team_change_mode(struct team *team,
|
|||
}
|
||||
|
||||
team->mode = new_mode;
|
||||
memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
|
||||
team->ops.init = new_mode->ops->init;
|
||||
team->ops.exit = new_mode->ops->exit;
|
||||
team->ops.port_enter = new_mode->ops->port_enter;
|
||||
team->ops.port_leave = new_mode->ops->port_leave;
|
||||
team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr;
|
||||
team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled;
|
||||
team_adjust_ops(team);
|
||||
|
||||
return 0;
|
||||
|
|
@ -743,7 +762,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
|
|||
/* allow exact match delivery for disabled ports */
|
||||
res = RX_HANDLER_EXACT;
|
||||
} else {
|
||||
res = team->ops.receive(team, port, skb);
|
||||
res = READ_ONCE(team->ops.receive)(team, port, skb);
|
||||
}
|
||||
if (res == RX_HANDLER_ANOTHER) {
|
||||
struct team_pcpu_stats *pcpu_stats;
|
||||
|
|
@ -1845,7 +1864,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
|
||||
tx_success = team_queue_override_transmit(team, skb);
|
||||
if (!tx_success)
|
||||
tx_success = team->ops.transmit(team, skb);
|
||||
tx_success = READ_ONCE(team->ops.transmit)(team, skb);
|
||||
if (tx_success) {
|
||||
struct team_pcpu_stats *pcpu_stats;
|
||||
|
||||
|
|
|
|||
|
|
@ -2394,8 +2394,10 @@ static int tun_xdp_one(struct tun_struct *tun,
|
|||
bool skb_xdp = false;
|
||||
struct page *page;
|
||||
|
||||
if (unlikely(datasize < ETH_HLEN))
|
||||
if (unlikely(datasize < ETH_HLEN)) {
|
||||
put_page(virt_to_head_page(xdp->data));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xdp_prog = rcu_dereference(tun->xdp_prog);
|
||||
if (xdp_prog) {
|
||||
|
|
@ -2437,6 +2439,7 @@ static int tun_xdp_one(struct tun_struct *tun,
|
|||
build:
|
||||
skb = build_skb(xdp->data_hard_start, buflen);
|
||||
if (!skb) {
|
||||
put_page(virt_to_head_page(xdp->data));
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2531,7 +2531,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
|
||||
tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
|
||||
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
|
||||
err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr),
|
||||
vni, md, flags, udp_sum);
|
||||
|
|
@ -2605,7 +2605,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
|
||||
tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
|
||||
ttl = ttl ? : ip6_dst_hoplimit(ndst);
|
||||
skb_scrub_packet(skb, xnet);
|
||||
err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nfc.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
|
@ -267,6 +268,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client)
|
|||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct nxp_nci_i2c_phy *phy;
|
||||
unsigned long irqflags;
|
||||
int r;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
|
|
@ -303,9 +305,26 @@ static int nxp_nci_i2c_probe(struct i2c_client *client)
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* ACPI platforms may report incorrect IRQ trigger types
|
||||
* (e.g. level-high), which can lead to interrupt storms.
|
||||
*
|
||||
* Use the historically stable rising-edge trigger for ACPI devices.
|
||||
*
|
||||
* On non-ACPI systems (e.g. Device Tree), prefer the firmware-
|
||||
* provided trigger type, falling back to rising-edge if not set.
|
||||
*/
|
||||
if (ACPI_COMPANION(dev)) {
|
||||
irqflags = IRQF_TRIGGER_RISING;
|
||||
} else {
|
||||
irqflags = irq_get_trigger_type(client->irq);
|
||||
if (!irqflags)
|
||||
irqflags = IRQF_TRIGGER_RISING;
|
||||
}
|
||||
|
||||
r = request_threaded_irq(client->irq, NULL,
|
||||
nxp_nci_i2c_irq_thread_fn,
|
||||
IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
NXP_NCI_I2C_DRIVER_NAME, phy);
|
||||
if (r < 0)
|
||||
nfc_err(&client->dev, "Unable to register IRQ handler\n");
|
||||
|
|
|
|||
|
|
@ -1702,7 +1702,7 @@ static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid)
|
|||
qid, pskid, status);
|
||||
|
||||
if (status) {
|
||||
queue->tls_err = -status;
|
||||
queue->tls_err = status;
|
||||
goto out_complete;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
|
|||
int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin,
|
||||
struct dpll_pin *ref_sync_pin);
|
||||
|
||||
int __dpll_device_change_ntf(struct dpll_device *dpll);
|
||||
int dpll_device_change_ntf(struct dpll_device *dpll);
|
||||
|
||||
int __dpll_pin_change_ntf(struct dpll_pin *pin);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ struct nf_ct_gre_keymap {
|
|||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
/* add new tuple->key_reply pair to keymap */
|
||||
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
|
||||
struct nf_conntrack_tuple *t);
|
||||
/* add tuple->key_reply pairs to keymap */
|
||||
bool nf_ct_gre_keymap_add(struct nf_conn *ct,
|
||||
const struct nf_conntrack_tuple *orig,
|
||||
const struct nf_conntrack_tuple *repl);
|
||||
|
||||
/* delete keymap entries */
|
||||
void nf_ct_gre_keymap_destroy(struct nf_conn *ct);
|
||||
|
|
|
|||
|
|
@ -821,6 +821,7 @@ enum skb_tstamp_type {
|
|||
* @_sk_redir: socket redirection information for skmsg
|
||||
* @_nfct: Associated connection, if any (with nfctinfo bits)
|
||||
* @skb_iif: ifindex of device we arrived on
|
||||
* @tc_depth: counter for packet duplication
|
||||
* @tc_index: Traffic control index
|
||||
* @hash: the packet hash
|
||||
* @queue_mapping: Queue mapping for multiqueue devices
|
||||
|
|
@ -1030,6 +1031,7 @@ struct sk_buff {
|
|||
__u8 csum_not_inet:1;
|
||||
#endif
|
||||
__u8 unreadable:1;
|
||||
__u8 tc_depth:2;
|
||||
#if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
|
||||
__u16 tc_index; /* traffic control index */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -180,6 +180,13 @@ static inline u64 nft_reg_load64(const u32 *sreg)
|
|||
return get_unaligned((u64 *)sreg);
|
||||
}
|
||||
|
||||
static inline bool nft_reg_overlap(u8 src, u8 dst, u32 len)
|
||||
{
|
||||
unsigned int n = DIV_ROUND_UP(len, sizeof(u32));
|
||||
|
||||
return src != dst && src < dst + n && dst < src + n;
|
||||
}
|
||||
|
||||
static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
|
||||
unsigned int len)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1000,19 +1000,25 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
|
|||
br_port_flags_change(p, changed_mask);
|
||||
|
||||
if (tb[IFLA_BRPORT_COST]) {
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (tb[IFLA_BRPORT_PRIORITY]) {
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY]));
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (tb[IFLA_BRPORT_STATE]) {
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE]));
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1114,9 +1120,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
err = br_setport(p, tb, extack);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
} else {
|
||||
/* Binary compatibility with old RSTP */
|
||||
if (nla_len(protinfo) < sizeof(u8))
|
||||
|
|
@ -1203,17 +1207,10 @@ static int br_port_slave_changelink(struct net_device *brdev,
|
|||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net_bridge *br = netdev_priv(brdev);
|
||||
int ret;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
ret = br_setport(br_port_get_rtnl(dev), data, extack);
|
||||
spin_unlock_bh(&br->lock);
|
||||
|
||||
return ret;
|
||||
return br_setport(br_port_get_rtnl(dev), data, extack);
|
||||
}
|
||||
|
||||
static int br_port_fill_slave_info(struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
|
|||
attr.u.brport_flags.val = flags;
|
||||
attr.u.brport_flags.mask = mask;
|
||||
|
||||
/* We run from atomic context here */
|
||||
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
|
||||
&info.info, extack);
|
||||
err = notifier_to_errno(err);
|
||||
|
|
|
|||
|
|
@ -86,16 +86,34 @@ static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
|
|||
return sysfs_emit(buf, "%d\n", p->path_cost);
|
||||
}
|
||||
|
||||
static BRPORT_ATTR(path_cost, 0644,
|
||||
show_path_cost, br_stp_set_path_cost);
|
||||
static int store_path_cost(struct net_bridge_port *p, unsigned long v)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
ret = br_stp_set_path_cost(p, v);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BRPORT_ATTR(path_cost, 0644, show_path_cost, store_path_cost);
|
||||
|
||||
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%d\n", p->priority);
|
||||
}
|
||||
|
||||
static BRPORT_ATTR(priority, 0644,
|
||||
show_priority, br_stp_set_port_priority);
|
||||
static int store_priority(struct net_bridge_port *p, unsigned long v)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&p->br->lock);
|
||||
ret = br_stp_set_port_priority(p, v);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BRPORT_ATTR(priority, 0644, show_priority, store_priority);
|
||||
|
||||
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
|
||||
{
|
||||
|
|
@ -334,17 +352,13 @@ static ssize_t brport_store(struct kobject *kobj,
|
|||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
spin_lock_bh(&p->br->lock);
|
||||
ret = brport_attr->store_raw(p, buf_copy);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
kfree(buf_copy);
|
||||
} else if (brport_attr->store) {
|
||||
val = simple_strtoul(buf, &endp, 0);
|
||||
if (endp == buf)
|
||||
goto out_unlock;
|
||||
spin_lock_bh(&p->br->lock);
|
||||
ret = brport_attr->store(p, val);
|
||||
spin_unlock_bh(&p->br->lock);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
|
|
|
|||
|
|
@ -1956,6 +1956,25 @@ enum compat_mwt {
|
|||
EBT_COMPAT_TARGET,
|
||||
};
|
||||
|
||||
static bool match_size_ok(const struct xt_match *match, unsigned int match_size)
|
||||
{
|
||||
u16 csize;
|
||||
|
||||
if (match->matchsize == -1) /* cannot validate ebt_among */
|
||||
return true;
|
||||
|
||||
csize = match->compatsize ? : match->matchsize;
|
||||
|
||||
return match_size >= csize;
|
||||
}
|
||||
|
||||
static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size)
|
||||
{
|
||||
u16 csize = tgt->compatsize ? : tgt->targetsize;
|
||||
|
||||
return tgt_size >= csize;
|
||||
}
|
||||
|
||||
static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
|
||||
enum compat_mwt compat_mwt,
|
||||
struct ebt_entries_buf_state *state,
|
||||
|
|
@ -1981,6 +2000,11 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
|
|||
if (IS_ERR(match))
|
||||
return PTR_ERR(match);
|
||||
|
||||
if (!match_size_ok(match, match_size)) {
|
||||
module_put(match->me);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
off = ebt_compat_match_offset(match, match_size);
|
||||
if (dst) {
|
||||
if (match->compat_from_user)
|
||||
|
|
@ -2000,6 +2024,12 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
|
|||
mwt->u.revision);
|
||||
if (IS_ERR(wt))
|
||||
return PTR_ERR(wt);
|
||||
|
||||
if (!tgt_size_ok(wt, match_size)) {
|
||||
module_put(wt->me);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
off = xt_compat_target_offset(wt);
|
||||
|
||||
if (dst) {
|
||||
|
|
|
|||
|
|
@ -2787,6 +2787,8 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len)
|
|||
skb->data_len = 0;
|
||||
skb_set_tail_pointer(skb, len);
|
||||
}
|
||||
if (!skb_shinfo(skb)->nr_frags && !skb_has_frag_list(skb))
|
||||
skb->unreadable = 0;
|
||||
|
||||
if (!skb->sk || skb->destructor == sock_edemux)
|
||||
skb_condense(skb);
|
||||
|
|
@ -2794,16 +2796,37 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len)
|
|||
}
|
||||
EXPORT_SYMBOL(___pskb_trim);
|
||||
|
||||
static int pskb_trim_rcsum_complete(struct sk_buff *skb, unsigned int len)
|
||||
{
|
||||
int delta = skb->len - len;
|
||||
|
||||
if (skb_frags_readable(skb)) {
|
||||
skb->csum = csum_block_sub(skb->csum,
|
||||
skb_checksum(skb, len, delta, 0),
|
||||
len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len > skb_headlen(skb))
|
||||
return -EFAULT;
|
||||
|
||||
/* The trimmed bytes are unreadable, but the remaining packet can be
|
||||
* checksummed by software after trimming.
|
||||
*/
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note : use pskb_trim_rcsum() instead of calling this directly
|
||||
*/
|
||||
int pskb_trim_rcsum_slow(struct sk_buff *skb, unsigned int len)
|
||||
{
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE) {
|
||||
int delta = skb->len - len;
|
||||
int err;
|
||||
|
||||
skb->csum = csum_block_sub(skb->csum,
|
||||
skb_checksum(skb, len, delta, 0),
|
||||
len);
|
||||
err = pskb_trim_rcsum_complete(skb, len);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
int hdlen = (len > skb_headlen(skb)) ? skb_headlen(skb) : len;
|
||||
int offset = skb_checksum_start_offset(skb) + skb->csum_offset;
|
||||
|
|
@ -6810,6 +6833,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
|
|||
skb_kfree_head(data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (skb_zcopy(skb))
|
||||
net_zcopy_get(skb_zcopy(skb));
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
|
||||
skb_frag_ref(skb, i);
|
||||
if (skb_has_frag_list(skb))
|
||||
|
|
@ -6953,6 +6978,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
|
|||
skb_kfree_head(data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (skb_zcopy(skb))
|
||||
net_zcopy_get(skb_zcopy(skb));
|
||||
skb_release_data(skb, SKB_CONSUMED);
|
||||
|
||||
skb->head = data;
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ struct ethtool_cmis_cdb_request {
|
|||
* struct ethtool_cmis_cdb_cmd_args - CDB commands execution arguments
|
||||
* @req: CDB command fields as described in the CMIS standard.
|
||||
* @max_duration: Maximum duration time for command completion in msec.
|
||||
* @msleep_pre_rpl: Waiting time before checking reply in msec.
|
||||
* @read_write_len_ext: Allowable additional number of byte octets to the LPL
|
||||
* in a READ or a WRITE commands.
|
||||
* @msleep_pre_rpl: Waiting time before checking reply in msec.
|
||||
* @rpl_exp_len: Expected reply length in bytes.
|
||||
* @flags: Validation flags for CDB commands.
|
||||
* @err_msg: Error message to be sent to user space.
|
||||
|
|
@ -73,8 +73,8 @@ struct ethtool_cmis_cdb_request {
|
|||
struct ethtool_cmis_cdb_cmd_args {
|
||||
struct ethtool_cmis_cdb_request req;
|
||||
u16 max_duration;
|
||||
u16 msleep_pre_rpl;
|
||||
u8 read_write_len_ext;
|
||||
u8 msleep_pre_rpl;
|
||||
u8 rpl_exp_len;
|
||||
u8 flags;
|
||||
char *err_msg;
|
||||
|
|
|
|||
|
|
@ -513,8 +513,13 @@ static int cmis_cdb_process_reply(struct net_device *dev,
|
|||
}
|
||||
|
||||
rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data;
|
||||
if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) ||
|
||||
!rpl->hdr.rpl_chk_code) {
|
||||
if (rpl->hdr.rpl_len != args->rpl_exp_len) {
|
||||
netdev_warn(dev, "CDB reply length mismatch, expected %u got %u\n",
|
||||
args->rpl_exp_len, rpl->hdr.rpl_len);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (!rpl->hdr.rpl_chk_code) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,20 @@ enum cmis_cdb_fw_write_mechanism {
|
|||
CMIS_CDB_FW_WRITE_MECHANISM_BOTH = 0x11,
|
||||
};
|
||||
|
||||
/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
|
||||
* revision 5.2.
|
||||
* struct cmis_cdb_start_fw_download_pl is a structured layout of the
|
||||
* flat array, ethtool_cmis_cdb_request::payload.
|
||||
*/
|
||||
struct cmis_cdb_start_fw_download_pl {
|
||||
__struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
|
||||
__be32 image_size;
|
||||
__be32 resv1;
|
||||
);
|
||||
u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
|
||||
sizeof(struct cmis_cdb_start_fw_download_pl_h)];
|
||||
};
|
||||
|
||||
static int
|
||||
cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
|
||||
struct net_device *dev,
|
||||
|
|
@ -86,6 +100,14 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
|
|||
*/
|
||||
cdb->read_write_len_ext = rpl->read_write_len_ext;
|
||||
fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size;
|
||||
if (fw_mng->start_cmd_payload_size >
|
||||
sizeof_field(struct cmis_cdb_start_fw_download_pl, vendor_data)) {
|
||||
ethnl_module_fw_flash_ntf_err(dev, ntf_params,
|
||||
"Start cmd payload size exceeds max LPL payload",
|
||||
NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw_mng->write_mechanism =
|
||||
rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ?
|
||||
CMIS_CDB_FW_WRITE_MECHANISM_LPL :
|
||||
|
|
@ -97,20 +119,6 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
|
||||
* revision 5.2.
|
||||
* struct cmis_cdb_start_fw_download_pl is a structured layout of the
|
||||
* flat array, ethtool_cmis_cdb_request::payload.
|
||||
*/
|
||||
struct cmis_cdb_start_fw_download_pl {
|
||||
__struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
|
||||
__be32 image_size;
|
||||
__be32 resv1;
|
||||
);
|
||||
u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
|
||||
sizeof(struct cmis_cdb_start_fw_download_pl_h)];
|
||||
};
|
||||
|
||||
static int
|
||||
cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
|
||||
struct ethtool_cmis_fw_update_params *fw_update,
|
||||
|
|
@ -122,6 +130,14 @@ cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
|
|||
u8 lpl_len;
|
||||
int err;
|
||||
|
||||
if (fw_update->fw->size < vendor_data_size) {
|
||||
ethnl_module_fw_flash_ntf_err(fw_update->dev,
|
||||
&fw_update->ntf_params,
|
||||
"Firmware image too small for module's start payload",
|
||||
NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pl.image_size = cpu_to_be32(fw_update->fw->size);
|
||||
memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size);
|
||||
|
||||
|
|
|
|||
|
|
@ -472,6 +472,12 @@ static int ethnl_update_profile(struct net_device *dev,
|
|||
|
||||
nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
|
||||
nests, rem) {
|
||||
if (i >= NET_DIM_PARAMS_NUM_PROFILES) {
|
||||
NL_SET_BAD_ATTR(extack, nest);
|
||||
ret = -E2BIG;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
|
||||
coalesce_irq_moderation_policy,
|
||||
extack);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ static int fallback_set_params(struct eeprom_req_info *request,
|
|||
if (offset >= modinfo->eeprom_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (length > modinfo->eeprom_len - offset)
|
||||
return -EINVAL;
|
||||
|
||||
eeprom->cmd = ETHTOOL_GMODULEEEPROM;
|
||||
eeprom->len = length;
|
||||
eeprom->offset = offset;
|
||||
|
|
@ -69,7 +72,7 @@ static int eeprom_fallback(struct eeprom_req_info *request,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = kmalloc(eeprom.len, GFP_KERNEL);
|
||||
data = kzalloc(eeprom.len, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
err = ethtool_get_module_eeprom_call(dev, &eeprom, data);
|
||||
|
|
@ -141,12 +144,11 @@ static int eeprom_prepare_data(const struct ethnl_req_info *req_base,
|
|||
return 0;
|
||||
|
||||
err_ops:
|
||||
if (ret == -EOPNOTSUPP)
|
||||
ret = eeprom_fallback(request, reply);
|
||||
ethnl_ops_complete(dev);
|
||||
err_free:
|
||||
kfree(page_data.data);
|
||||
|
||||
if (ret == -EOPNOTSUPP)
|
||||
return eeprom_fallback(request, reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,10 +106,8 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
|
|||
|
||||
phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_LINKSTATE_HEADER,
|
||||
info->extack);
|
||||
if (IS_ERR(phydev)) {
|
||||
ret = PTR_ERR(phydev);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(phydev))
|
||||
return PTR_ERR(phydev);
|
||||
|
||||
ret = ethnl_ops_begin(dev);
|
||||
if (ret < 0)
|
||||
|
|
|
|||
|
|
@ -120,12 +120,6 @@ ethnl_set_module_validate(struct ethnl_req_info *req_info,
|
|||
if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
|
||||
return 0;
|
||||
|
||||
if (req_info->dev->ethtool->module_fw_flash_in_progress) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"Module firmware flashing is in progress");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
|
||||
|
|
@ -148,6 +142,12 @@ ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info)
|
|||
|
||||
ops = dev->ethtool_ops;
|
||||
|
||||
if (dev->ethtool->module_fw_flash_in_progress) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"Module firmware flashing is in progress");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
|
||||
ret = ops->get_module_power_mode(dev, &power, info->extack);
|
||||
if (ret < 0)
|
||||
|
|
@ -221,14 +221,22 @@ static void module_flash_fw_work_list_del(struct list_head *list)
|
|||
static void module_flash_fw_work(struct work_struct *work)
|
||||
{
|
||||
struct ethtool_module_fw_flash *module_fw;
|
||||
struct net_device *dev;
|
||||
|
||||
module_fw = container_of(work, struct ethtool_module_fw_flash, work);
|
||||
dev = module_fw->fw_update.dev;
|
||||
|
||||
ethtool_cmis_fw_update(&module_fw->fw_update);
|
||||
|
||||
module_flash_fw_work_list_del(&module_fw->list);
|
||||
module_fw->fw_update.dev->ethtool->module_fw_flash_in_progress = false;
|
||||
netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker);
|
||||
|
||||
rtnl_lock();
|
||||
netdev_lock_ops(dev);
|
||||
dev->ethtool->module_fw_flash_in_progress = false;
|
||||
netdev_unlock_ops(dev);
|
||||
rtnl_unlock();
|
||||
|
||||
netdev_put(dev, &module_fw->dev_tracker);
|
||||
release_firmware(module_fw->fw_update.fw);
|
||||
kfree(module_fw);
|
||||
}
|
||||
|
|
@ -283,11 +291,9 @@ void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv)
|
|||
|
||||
spin_lock(&module_fw_flash_work_list_lock);
|
||||
list_for_each_entry(work, &module_fw_flash_work_list, list) {
|
||||
if (work->fw_update.dev == sk_priv->dev &&
|
||||
work->fw_update.ntf_params.portid == sk_priv->portid) {
|
||||
if (work->fw_update.ntf_params.portid == sk_priv->portid &&
|
||||
dev_net(work->fw_update.dev) == sk_priv->net)
|
||||
work->fw_update.ntf_params.closed_sock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&module_fw_flash_work_list_lock);
|
||||
}
|
||||
|
|
@ -319,14 +325,13 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
|
|||
if (err < 0)
|
||||
goto err_release_firmware;
|
||||
|
||||
dev->ethtool->module_fw_flash_in_progress = true;
|
||||
netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
|
||||
fw_update->dev = dev;
|
||||
fw_update->ntf_params.portid = info->snd_portid;
|
||||
fw_update->ntf_params.seq = info->snd_seq;
|
||||
fw_update->ntf_params.closed_sock = false;
|
||||
|
||||
err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid,
|
||||
err = ethnl_sock_priv_set(skb, dev_net(dev),
|
||||
fw_update->ntf_params.portid,
|
||||
ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH);
|
||||
if (err < 0)
|
||||
goto err_release_firmware;
|
||||
|
|
@ -335,6 +340,9 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
|
|||
if (err < 0)
|
||||
goto err_release_firmware;
|
||||
|
||||
dev->ethtool->module_fw_flash_in_progress = true;
|
||||
netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
|
||||
|
||||
schedule_work(&module_fw->work);
|
||||
|
||||
return 0;
|
||||
|
|
@ -427,10 +435,11 @@ int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
ret = ethnl_module_fw_flash_validate(dev, info->extack);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
goto out_complete;
|
||||
|
||||
ret = module_flash_fw(dev, tb, skb, info);
|
||||
|
||||
out_complete:
|
||||
ethnl_ops_complete(dev);
|
||||
|
||||
out_unlock:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ const struct nla_policy ethnl_header_policy_phy_stats[] = {
|
|||
[ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
};
|
||||
|
||||
int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
|
||||
int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid,
|
||||
enum ethnl_sock_type type)
|
||||
{
|
||||
struct ethnl_sock_priv *sk_priv;
|
||||
|
|
@ -62,7 +62,7 @@ int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
|
|||
if (IS_ERR(sk_priv))
|
||||
return PTR_ERR(sk_priv);
|
||||
|
||||
sk_priv->dev = dev;
|
||||
sk_priv->net = net;
|
||||
sk_priv->portid = portid;
|
||||
sk_priv->type = type;
|
||||
|
||||
|
|
|
|||
|
|
@ -318,12 +318,12 @@ enum ethnl_sock_type {
|
|||
};
|
||||
|
||||
struct ethnl_sock_priv {
|
||||
struct net_device *dev;
|
||||
struct net *net;
|
||||
u32 portid;
|
||||
enum ethnl_sock_type type;
|
||||
};
|
||||
|
||||
int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
|
||||
int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid,
|
||||
enum ethnl_sock_type type);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base,
|
|||
struct phy_device *phydev;
|
||||
int ret;
|
||||
|
||||
ret = ethnl_ops_begin(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PSE_HEADER,
|
||||
info->extack);
|
||||
if (IS_ERR(phydev))
|
||||
return -ENODEV;
|
||||
return PTR_ERR(phydev);
|
||||
|
||||
ret = ethnl_ops_begin(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = pse_get_pse_attributes(phydev, info->extack, data);
|
||||
|
||||
|
|
|
|||
|
|
@ -134,8 +134,7 @@ rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data)
|
|||
if (!rss_config)
|
||||
return -ENOMEM;
|
||||
|
||||
if (data->indir_size)
|
||||
data->indir_table = (u32 *)rss_config;
|
||||
data->indir_table = (u32 *)rss_config;
|
||||
if (data->hkey_size)
|
||||
data->hkey = rss_config + indir_bytes;
|
||||
|
||||
|
|
@ -170,8 +169,10 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
|
|||
rxfh.key = data->hkey;
|
||||
|
||||
ret = ops->get_rxfh(dev, &rxfh);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
rss_get_data_free(data);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
data->hfunc = rxfh.hfunc;
|
||||
data->input_xfrm = rxfh.input_xfrm;
|
||||
|
|
@ -686,7 +687,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
|
|||
ethtool_rxfh_indir_default(i, num_rx_rings);
|
||||
}
|
||||
|
||||
*mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
|
||||
*mod |= memcmp(rxfh->indir, data->indir_table, alloc_size);
|
||||
|
||||
return user_size;
|
||||
|
||||
|
|
@ -981,11 +982,17 @@ ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info)
|
|||
}
|
||||
|
||||
static void
|
||||
ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev)
|
||||
ethnl_rss_create_send_ntf(const struct sk_buff *rsp, struct net_device *dev)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *)rsp->data;
|
||||
struct genlmsghdr *genl_hdr;
|
||||
struct nlmsghdr *nlh;
|
||||
struct sk_buff *ntf;
|
||||
|
||||
ntf = skb_copy_expand(rsp, 0, 0, GFP_KERNEL);
|
||||
if (!ntf)
|
||||
return;
|
||||
|
||||
nlh = nlmsg_hdr(ntf);
|
||||
/* Convert the reply into a notification */
|
||||
nlh->nlmsg_pid = 0;
|
||||
nlh->nlmsg_seq = ethnl_bcast_seq_next();
|
||||
|
|
@ -993,7 +1000,7 @@ ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev)
|
|||
genl_hdr = nlmsg_data(nlh);
|
||||
genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF;
|
||||
|
||||
ethnl_multicast(rsp, dev);
|
||||
ethnl_multicast(ntf, dev);
|
||||
}
|
||||
|
||||
int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
|
|
@ -1099,17 +1106,13 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base);
|
||||
if (WARN_ON(!hdr || ntf_fail)) {
|
||||
ret = -EMSGSIZE;
|
||||
goto exit_unlock;
|
||||
goto err_remove_ctx;
|
||||
}
|
||||
|
||||
genlmsg_end(rsp, hdr);
|
||||
|
||||
/* Use the same skb for the response and the notification,
|
||||
* genlmsg_reply() will copy the skb if it has elevated user count.
|
||||
*/
|
||||
skb_get(rsp);
|
||||
ret = genlmsg_reply(rsp, info);
|
||||
ethnl_rss_create_send_ntf(rsp, dev);
|
||||
ret = genlmsg_reply(rsp, info);
|
||||
rsp = NULL;
|
||||
|
||||
exit_unlock:
|
||||
|
|
@ -1131,6 +1134,10 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
nlmsg_free(rsp);
|
||||
return ret;
|
||||
|
||||
err_remove_ctx:
|
||||
if (ops->remove_rxfh_context(dev, ctx, req.rss_context, NULL))
|
||||
/* leave the context on failure, like ethnl_rss_delete_doit() */
|
||||
goto exit_unlock;
|
||||
err_ctx_id_free:
|
||||
xa_erase(&dev->ethtool->rss_ctx, req.rss_context);
|
||||
err_unlock_free_ctx:
|
||||
|
|
@ -1168,8 +1175,10 @@ int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
dev = req.dev;
|
||||
ops = dev->ethtool_ops;
|
||||
|
||||
if (!ops->create_rxfh_context)
|
||||
if (!ops->create_rxfh_context) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto exit_free_dev;
|
||||
}
|
||||
|
||||
rtnl_lock();
|
||||
netdev_lock_ops(dev);
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base,
|
|||
return 0;
|
||||
}
|
||||
|
||||
phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS,
|
||||
phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_STRSET_HEADER,
|
||||
info->extack);
|
||||
|
||||
/* phydev can be NULL, check for errors only */
|
||||
|
|
|
|||
|
|
@ -69,8 +69,10 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (ts_info.phc_index == -1)
|
||||
return -ENODEV;
|
||||
if (ts_info.phc_index == -1) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->hwprov_desc.index = ts_info.phc_index;
|
||||
data->hwprov_desc.qualifier = ts_info.phc_qualifier;
|
||||
|
|
@ -224,16 +226,21 @@ static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info)
|
|||
reply_len = ret + ethnl_reply_header_size();
|
||||
rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_MSG_TSCONFIG_SET_REPLY,
|
||||
ETHTOOL_A_TSCONFIG_HEADER, info, &reply_payload);
|
||||
if (!rskb)
|
||||
if (!rskb) {
|
||||
ret = -ENOMEM;
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
ret = tsconfig_fill_reply(rskb, &req_info->base, &reply_data->base);
|
||||
if (ret < 0)
|
||||
goto err_cleanup;
|
||||
goto err_free_msg;
|
||||
|
||||
genlmsg_end(rskb, reply_payload);
|
||||
ret = genlmsg_reply(rskb, info);
|
||||
rskb = NULL;
|
||||
|
||||
err_free_msg:
|
||||
nlmsg_free(rskb);
|
||||
err_cleanup:
|
||||
kfree(reply_data);
|
||||
kfree(req_info);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ tsinfo_parse_request(struct ethnl_req_info *req_base,
|
|||
if (!tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER])
|
||||
return 0;
|
||||
|
||||
if (req_base->flags & ETHTOOL_FLAG_STATS) {
|
||||
NL_SET_ERR_MSG(extack, "can't query statistics for a provider");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ts_parse_hwtst_provider(tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER],
|
||||
&req->hwprov_desc, extack, &mod);
|
||||
}
|
||||
|
|
@ -402,10 +407,8 @@ static int ethnl_tsinfo_dump_one_netdev(struct sk_buff *skb,
|
|||
continue;
|
||||
|
||||
ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb);
|
||||
if (IS_ERR(ehdr)) {
|
||||
ret = PTR_ERR(ehdr);
|
||||
goto err;
|
||||
}
|
||||
if (IS_ERR(ehdr))
|
||||
return PTR_ERR(ehdr);
|
||||
|
||||
reply_data->ts_info.phc_qualifier = ctx->pos_phcqualifier;
|
||||
ret = ops->get_ts_info(dev, &reply_data->ts_info);
|
||||
|
|
@ -523,6 +526,12 @@ int ethnl_tsinfo_start(struct netlink_callback *cb)
|
|||
if (ret < 0)
|
||||
goto free_reply_data;
|
||||
|
||||
if (req_info->base.flags & ETHTOOL_FLAG_STATS) {
|
||||
NL_SET_ERR_MSG(cb->extack, "stats not supported in dump");
|
||||
ret = -EOPNOTSUPP;
|
||||
goto err_dev_put;
|
||||
}
|
||||
|
||||
ctx->req_info = req_info;
|
||||
ctx->reply_data = reply_data;
|
||||
ctx->pos_ifindex = 0;
|
||||
|
|
@ -532,6 +541,8 @@ int ethnl_tsinfo_start(struct netlink_callback *cb)
|
|||
|
||||
return 0;
|
||||
|
||||
err_dev_put:
|
||||
ethnl_parse_header_dev_put(&req_info->base);
|
||||
free_reply_data:
|
||||
kfree(reply_data);
|
||||
free_req_info:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "genl.h"
|
||||
|
||||
#include <uapi/linux/handshake.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* HANDSHAKE_CMD_ACCEPT - do */
|
||||
static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = {
|
||||
|
|
@ -18,7 +19,7 @@ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HAN
|
|||
|
||||
/* HANDSHAKE_CMD_DONE - do */
|
||||
static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_REMOTE_AUTH + 1] = {
|
||||
[HANDSHAKE_A_DONE_STATUS] = { .type = NLA_U32, },
|
||||
[HANDSHAKE_A_DONE_STATUS] = NLA_POLICY_MAX(NLA_U32, MAX_ERRNO),
|
||||
[HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_S32, },
|
||||
[HANDSHAKE_A_DONE_REMOTE_AUTH] = { .type = NLA_U32, },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <net/genetlink.h>
|
||||
|
||||
#include <uapi/linux/handshake.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ static int test_accept_func(struct handshake_req *req, struct genl_info *info,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void test_done_func(struct handshake_req *req, unsigned int status,
|
||||
static void test_done_func(struct handshake_req *req, int status,
|
||||
struct genl_info *info)
|
||||
{
|
||||
}
|
||||
|
|
@ -208,6 +208,7 @@ static void handshake_req_submit_test3(struct kunit *test)
|
|||
static void handshake_req_submit_test4(struct kunit *test)
|
||||
{
|
||||
struct handshake_req *req, *result;
|
||||
unsigned long fcount_before;
|
||||
struct socket *sock;
|
||||
struct file *filp;
|
||||
int err;
|
||||
|
|
@ -224,8 +225,10 @@ static void handshake_req_submit_test4(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_NULL(test, sock->sk);
|
||||
sock->file = filp;
|
||||
|
||||
fcount_before = file_count(filp);
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
|
||||
/* Act */
|
||||
result = handshake_req_hash_lookup(sock->sk);
|
||||
|
|
@ -235,11 +238,13 @@ static void handshake_req_submit_test4(struct kunit *test)
|
|||
KUNIT_EXPECT_PTR_EQ(test, req, result);
|
||||
|
||||
handshake_req_cancel(sock->sk);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
fput(filp);
|
||||
}
|
||||
|
||||
static void handshake_req_submit_test5(struct kunit *test)
|
||||
{
|
||||
unsigned long fcount_before;
|
||||
struct handshake_req *req;
|
||||
struct handshake_net *hn;
|
||||
struct socket *sock;
|
||||
|
|
@ -265,12 +270,14 @@ static void handshake_req_submit_test5(struct kunit *test)
|
|||
|
||||
saved = hn->hn_pending;
|
||||
hn->hn_pending = hn->hn_pending_max + 1;
|
||||
fcount_before = file_count(filp);
|
||||
|
||||
/* Act */
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
|
||||
/* Assert */
|
||||
KUNIT_EXPECT_EQ(test, err, -EAGAIN);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
fput(filp);
|
||||
hn->hn_pending = saved;
|
||||
|
|
@ -279,6 +286,7 @@ static void handshake_req_submit_test5(struct kunit *test)
|
|||
static void handshake_req_submit_test6(struct kunit *test)
|
||||
{
|
||||
struct handshake_req *req1, *req2;
|
||||
unsigned long fcount_before;
|
||||
struct socket *sock;
|
||||
struct file *filp;
|
||||
int err;
|
||||
|
|
@ -296,21 +304,26 @@ static void handshake_req_submit_test6(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
|
||||
KUNIT_ASSERT_NOT_NULL(test, sock->sk);
|
||||
sock->file = filp;
|
||||
fcount_before = file_count(filp);
|
||||
|
||||
/* Act */
|
||||
err = handshake_req_submit(sock, req1, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
err = handshake_req_submit(sock, req2, GFP_KERNEL);
|
||||
|
||||
/* Assert */
|
||||
KUNIT_EXPECT_EQ(test, err, -EBUSY);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
|
||||
handshake_req_cancel(sock->sk);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
fput(filp);
|
||||
}
|
||||
|
||||
static void handshake_req_cancel_test1(struct kunit *test)
|
||||
{
|
||||
unsigned long fcount_before;
|
||||
struct handshake_req *req;
|
||||
struct socket *sock;
|
||||
struct file *filp;
|
||||
|
|
@ -329,8 +342,10 @@ static void handshake_req_cancel_test1(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
|
||||
sock->file = filp;
|
||||
|
||||
fcount_before = file_count(filp);
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
|
||||
/* NB: handshake_req hasn't been accepted */
|
||||
|
||||
|
|
@ -339,12 +354,14 @@ static void handshake_req_cancel_test1(struct kunit *test)
|
|||
|
||||
/* Assert */
|
||||
KUNIT_EXPECT_TRUE(test, result);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
fput(filp);
|
||||
}
|
||||
|
||||
static void handshake_req_cancel_test2(struct kunit *test)
|
||||
{
|
||||
unsigned long fcount_before;
|
||||
struct handshake_req *req, *next;
|
||||
struct handshake_net *hn;
|
||||
struct socket *sock;
|
||||
|
|
@ -365,8 +382,10 @@ static void handshake_req_cancel_test2(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
|
||||
sock->file = filp;
|
||||
|
||||
fcount_before = file_count(filp);
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
|
||||
net = sock_net(sock->sk);
|
||||
hn = handshake_pernet(net);
|
||||
|
|
@ -375,18 +394,24 @@ static void handshake_req_cancel_test2(struct kunit *test)
|
|||
/* Pretend to accept this request */
|
||||
next = handshake_req_next(hn, HANDSHAKE_HANDLER_CLASS_TLSHD);
|
||||
KUNIT_ASSERT_PTR_EQ(test, req, next);
|
||||
/* Simulate FD_PREPARE() consuming the file reference handed
|
||||
* off by handshake_req_next(); see handshake_nl_accept_doit().
|
||||
*/
|
||||
fput(filp);
|
||||
|
||||
/* Act */
|
||||
result = handshake_req_cancel(sock->sk);
|
||||
|
||||
/* Assert */
|
||||
KUNIT_EXPECT_TRUE(test, result);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
fput(filp);
|
||||
}
|
||||
|
||||
static void handshake_req_cancel_test3(struct kunit *test)
|
||||
{
|
||||
unsigned long fcount_before;
|
||||
struct handshake_req *req, *next;
|
||||
struct handshake_net *hn;
|
||||
struct socket *sock;
|
||||
|
|
@ -407,8 +432,10 @@ static void handshake_req_cancel_test3(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
|
||||
sock->file = filp;
|
||||
|
||||
fcount_before = file_count(filp);
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before + 1);
|
||||
|
||||
net = sock_net(sock->sk);
|
||||
hn = handshake_pernet(net);
|
||||
|
|
@ -417,15 +444,21 @@ static void handshake_req_cancel_test3(struct kunit *test)
|
|||
/* Pretend to accept this request */
|
||||
next = handshake_req_next(hn, HANDSHAKE_HANDLER_CLASS_TLSHD);
|
||||
KUNIT_ASSERT_PTR_EQ(test, req, next);
|
||||
/* Simulate FD_PREPARE() consuming the file reference handed
|
||||
* off by handshake_req_next(); see handshake_nl_accept_doit().
|
||||
*/
|
||||
fput(filp);
|
||||
|
||||
/* Pretend to complete this request */
|
||||
handshake_complete(next, -ETIMEDOUT, NULL);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
/* Act */
|
||||
result = handshake_req_cancel(sock->sk);
|
||||
|
||||
/* Assert */
|
||||
KUNIT_EXPECT_FALSE(test, result);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
fput(filp);
|
||||
}
|
||||
|
|
@ -446,6 +479,7 @@ static struct handshake_proto handshake_req_alloc_proto_destroy = {
|
|||
|
||||
static void handshake_req_destroy_test1(struct kunit *test)
|
||||
{
|
||||
unsigned long fcount_before;
|
||||
struct handshake_req *req;
|
||||
struct socket *sock;
|
||||
struct file *filp;
|
||||
|
|
@ -465,10 +499,12 @@ static void handshake_req_destroy_test1(struct kunit *test)
|
|||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
|
||||
sock->file = filp;
|
||||
|
||||
fcount_before = file_count(filp);
|
||||
err = handshake_req_submit(sock, req, GFP_KERNEL);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
|
||||
handshake_req_cancel(sock->sk);
|
||||
KUNIT_EXPECT_EQ(test, file_count(filp), fcount_before);
|
||||
|
||||
/* Act */
|
||||
/* Ensure the close/release/put process has run to
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ enum hn_flags_bits {
|
|||
HANDSHAKE_F_NET_DRAINING,
|
||||
};
|
||||
|
||||
struct file;
|
||||
struct handshake_proto;
|
||||
|
||||
/* One handshake request */
|
||||
|
|
@ -32,6 +33,7 @@ struct handshake_req {
|
|||
struct rhash_head hr_rhash;
|
||||
unsigned long hr_flags;
|
||||
const struct handshake_proto *hr_proto;
|
||||
struct file *hr_file;
|
||||
struct sock *hr_sk;
|
||||
void (*hr_odestruct)(struct sock *sk);
|
||||
|
||||
|
|
@ -57,7 +59,7 @@ struct handshake_proto {
|
|||
int (*hp_accept)(struct handshake_req *req,
|
||||
struct genl_info *info, int fd);
|
||||
void (*hp_done)(struct handshake_req *req,
|
||||
unsigned int status,
|
||||
int status,
|
||||
struct genl_info *info);
|
||||
void (*hp_destroy)(struct handshake_req *req);
|
||||
};
|
||||
|
|
@ -86,7 +88,7 @@ struct handshake_req *handshake_req_hash_lookup(struct sock *sk);
|
|||
struct handshake_req *handshake_req_next(struct handshake_net *hn, int class);
|
||||
int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
||||
gfp_t flags);
|
||||
void handshake_complete(struct handshake_req *req, unsigned int status,
|
||||
void handshake_complete(struct handshake_req *req, int status,
|
||||
struct genl_info *info);
|
||||
bool handshake_req_cancel(struct sock *sk);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
struct net *net = sock_net(skb->sk);
|
||||
struct handshake_net *hn = handshake_pernet(net);
|
||||
struct handshake_req *req = NULL;
|
||||
struct socket *sock;
|
||||
int class, err;
|
||||
|
||||
err = -EOPNOTSUPP;
|
||||
|
|
@ -107,15 +106,13 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
err = -EAGAIN;
|
||||
req = handshake_req_next(hn, class);
|
||||
if (req) {
|
||||
sock = req->hr_sk->sk_socket;
|
||||
|
||||
FD_PREPARE(fdf, O_CLOEXEC, sock->file);
|
||||
FD_PREPARE(fdf, O_CLOEXEC, req->hr_file);
|
||||
if (fdf.err) {
|
||||
fput(req->hr_file); /* drop ref from handshake_req_next() */
|
||||
err = fdf.err;
|
||||
goto out_complete;
|
||||
}
|
||||
|
||||
get_file(sock->file); /* FD_PREPARE() consumes a reference. */
|
||||
err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf));
|
||||
if (err)
|
||||
goto out_complete; /* Automatic cleanup handles fput */
|
||||
|
|
@ -160,7 +157,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
status = -EIO;
|
||||
if (info->attrs[HANDSHAKE_A_DONE_STATUS])
|
||||
status = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
|
||||
status = -(int)nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
|
||||
|
||||
handshake_complete(req, status, info);
|
||||
sockfd_put(sock);
|
||||
|
|
@ -202,21 +199,21 @@ static void __net_exit handshake_net_exit(struct net *net)
|
|||
* accepted and are in progress will be destroyed when
|
||||
* the socket is closed.
|
||||
*/
|
||||
spin_lock(&hn->hn_lock);
|
||||
spin_lock_bh(&hn->hn_lock);
|
||||
set_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags);
|
||||
list_splice_init(&requests, &hn->hn_requests);
|
||||
spin_unlock(&hn->hn_lock);
|
||||
list_splice_init(&hn->hn_requests, &requests);
|
||||
list_for_each_entry(req, &requests, hr_list)
|
||||
get_file(req->hr_file);
|
||||
spin_unlock_bh(&hn->hn_lock);
|
||||
|
||||
while (!list_empty(&requests)) {
|
||||
struct file *file;
|
||||
|
||||
req = list_first_entry(&requests, struct handshake_req, hr_list);
|
||||
list_del(&req->hr_list);
|
||||
|
||||
/*
|
||||
* Requests on this list have not yet been
|
||||
* accepted, so they do not have an fd to put.
|
||||
*/
|
||||
|
||||
file = req->hr_file;
|
||||
list_del_init(&req->hr_list);
|
||||
handshake_complete(req, -ETIMEDOUT, NULL);
|
||||
fput(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/rhashtable.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
|
@ -162,35 +163,56 @@ static void __remove_pending_locked(struct handshake_net *hn,
|
|||
* otherwise %false.
|
||||
*
|
||||
* If @req was on a pending list, it has not yet been accepted.
|
||||
* Returns %false when the net namespace is draining; the drain
|
||||
* loop has taken ownership of the pending list.
|
||||
*/
|
||||
static bool remove_pending(struct handshake_net *hn, struct handshake_req *req)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
spin_lock(&hn->hn_lock);
|
||||
if (!list_empty(&req->hr_list)) {
|
||||
spin_lock_bh(&hn->hn_lock);
|
||||
if (!test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags) &&
|
||||
!list_empty(&req->hr_list)) {
|
||||
__remove_pending_locked(hn, req);
|
||||
ret = true;
|
||||
}
|
||||
spin_unlock(&hn->hn_lock);
|
||||
spin_unlock_bh(&hn->hn_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* handshake_req_next - Return the next queued handshake request
|
||||
* @hn: per-net handshake state
|
||||
* @class: handler class to match
|
||||
*
|
||||
* On a non-NULL return, the caller owns an extra reference
|
||||
* on @req->hr_file. FD_PREPARE() consumes it on success; on
|
||||
* the FD_PREPARE() failure path the caller must fput() it.
|
||||
*
|
||||
* Return: pointer to a removed handshake_req, or NULL.
|
||||
*/
|
||||
struct handshake_req *handshake_req_next(struct handshake_net *hn, int class)
|
||||
{
|
||||
struct handshake_req *req, *pos;
|
||||
|
||||
req = NULL;
|
||||
spin_lock(&hn->hn_lock);
|
||||
spin_lock_bh(&hn->hn_lock);
|
||||
list_for_each_entry(pos, &hn->hn_requests, hr_list) {
|
||||
if (pos->hr_proto->hp_handler_class != class)
|
||||
continue;
|
||||
__remove_pending_locked(hn, pos);
|
||||
/* Hand off a file reference to the accept side under
|
||||
* hn_lock. A concurrent handshake_req_cancel() can drop
|
||||
* hr_file before accept reaches FD_PREPARE(); this extra
|
||||
* reference keeps the file alive until FD_PREPARE() takes
|
||||
* ownership.
|
||||
*/
|
||||
get_file(pos->hr_file);
|
||||
req = pos;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&hn->hn_lock);
|
||||
spin_unlock_bh(&hn->hn_lock);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
|
@ -215,9 +237,16 @@ EXPORT_SYMBOL_IF_KUNIT(handshake_req_next);
|
|||
* A zero return value from handshake_req_submit() means that
|
||||
* exactly one subsequent completion callback is guaranteed.
|
||||
*
|
||||
* A negative return value from handshake_req_submit() means that
|
||||
* no completion callback will be done and that @req has been
|
||||
* destroyed.
|
||||
* A negative return value from handshake_req_submit() guarantees that
|
||||
* no completion callback will occur and that @req is no longer owned by
|
||||
* the caller. If cancellation wins the completion race after the request
|
||||
* has been published, final destruction is deferred until socket teardown.
|
||||
*
|
||||
* The caller must hold a reference on @sock->file for the duration
|
||||
* of this call. Once the request is published to the accept side, a
|
||||
* concurrent completion or cancellation may release the request's pin on
|
||||
* @sock->file; the caller's reference is what keeps @sock->sk valid until
|
||||
* handshake_req_submit() returns.
|
||||
*/
|
||||
int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
||||
gfp_t flags)
|
||||
|
|
@ -236,6 +265,14 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
|||
kfree(req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pin sock->file for the lifetime of the request so the
|
||||
* accept side does not race a consumer that releases the
|
||||
* socket while a handshake is pending.
|
||||
*/
|
||||
req->hr_file = get_file(sock->file);
|
||||
|
||||
req->hr_odestruct = req->hr_sk->sk_destruct;
|
||||
req->hr_sk->sk_destruct = handshake_sk_destruct;
|
||||
|
||||
|
|
@ -249,7 +286,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
|||
if (READ_ONCE(hn->hn_pending) >= hn->hn_pending_max)
|
||||
goto out_err;
|
||||
|
||||
spin_lock(&hn->hn_lock);
|
||||
spin_lock_bh(&hn->hn_lock);
|
||||
ret = -EOPNOTSUPP;
|
||||
if (test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags))
|
||||
goto out_unlock;
|
||||
|
|
@ -258,7 +295,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
|||
goto out_unlock;
|
||||
if (!__add_pending_locked(hn, req))
|
||||
goto out_unlock;
|
||||
spin_unlock(&hn->hn_lock);
|
||||
spin_unlock_bh(&hn->hn_lock);
|
||||
|
||||
ret = handshake_genl_notify(net, req->hr_proto, flags);
|
||||
if (ret) {
|
||||
|
|
@ -267,35 +304,36 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
/* Prevent socket release while a handshake request is pending */
|
||||
sock_hold(req->hr_sk);
|
||||
|
||||
trace_handshake_submit(net, req, req->hr_sk);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&hn->hn_lock);
|
||||
spin_unlock_bh(&hn->hn_lock);
|
||||
out_err:
|
||||
/* Restore original destructor so socket teardown still runs on failure */
|
||||
req->hr_sk->sk_destruct = req->hr_odestruct;
|
||||
trace_handshake_submit_err(net, req, req->hr_sk, ret);
|
||||
handshake_req_destroy(req);
|
||||
if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) {
|
||||
/* Restore original destructor so socket teardown still runs. */
|
||||
req->hr_sk->sk_destruct = req->hr_odestruct;
|
||||
fput(req->hr_file);
|
||||
handshake_req_destroy(req);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(handshake_req_submit);
|
||||
|
||||
void handshake_complete(struct handshake_req *req, unsigned int status,
|
||||
void handshake_complete(struct handshake_req *req, int status,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct sock *sk = req->hr_sk;
|
||||
struct net *net = sock_net(sk);
|
||||
|
||||
if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) {
|
||||
struct file *file = req->hr_file;
|
||||
|
||||
trace_handshake_complete(net, req, sk, status);
|
||||
req->hr_proto->hp_done(req, status, info);
|
||||
|
||||
/* Handshake request is no longer pending */
|
||||
sock_put(sk);
|
||||
fput(file);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(handshake_complete);
|
||||
|
|
@ -342,8 +380,7 @@ bool handshake_req_cancel(struct sock *sk)
|
|||
out_true:
|
||||
trace_handshake_cancel(net, req, sk);
|
||||
|
||||
/* Handshake request is no longer pending */
|
||||
sock_put(sk);
|
||||
fput(req->hr_file);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(handshake_req_cancel);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ static void tls_handshake_remote_peerids(struct tls_handshake_req *treq,
|
|||
*
|
||||
*/
|
||||
static void tls_handshake_done(struct handshake_req *req,
|
||||
unsigned int status, struct genl_info *info)
|
||||
int status, struct genl_info *info)
|
||||
{
|
||||
struct tls_handshake_req *treq = handshake_req_private(req);
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ static void tls_handshake_done(struct handshake_req *req,
|
|||
if (!status)
|
||||
set_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags);
|
||||
|
||||
treq->th_consumer_done(treq->th_consumer_data, -status,
|
||||
treq->th_consumer_done(treq->th_consumer_data, status,
|
||||
treq->th_peerid[0]);
|
||||
}
|
||||
|
||||
|
|
@ -425,6 +425,8 @@ EXPORT_SYMBOL(tls_server_hello_psk);
|
|||
* Request cancellation races with request completion. To determine
|
||||
* who won, callers examine the return value from this function.
|
||||
*
|
||||
* Context: May be called from process or softirq context.
|
||||
*
|
||||
* Return values:
|
||||
* %true - Uncompleted handshake request was canceled
|
||||
* %false - Handshake request already completed or not found
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
|
|||
|
||||
/* Get next tlv */
|
||||
total_length += hsr_sup_tag->tlv.HSR_TLV_length;
|
||||
if (!pskb_may_pull(skb, total_length))
|
||||
if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
|
||||
return false;
|
||||
skb_pull(skb, total_length);
|
||||
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
|
||||
|
|
@ -100,7 +100,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
|
|||
|
||||
/* make sure another tlv follows */
|
||||
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length;
|
||||
if (!pskb_may_pull(skb, total_length))
|
||||
if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
|
||||
return false;
|
||||
|
||||
/* get next tlv */
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ EXPORT_SYMBOL_GPL(iptunnel_handle_offloads);
|
|||
*/
|
||||
static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
|
||||
{
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
const struct iphdr *iph;
|
||||
struct icmphdr *icmph;
|
||||
struct iphdr *niph;
|
||||
struct ethhdr eh;
|
||||
|
|
@ -226,7 +226,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
|
|||
|
||||
skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);
|
||||
pskb_pull(skb, ETH_HLEN);
|
||||
skb_reset_network_header(skb);
|
||||
|
||||
err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph));
|
||||
if (err)
|
||||
|
|
@ -236,7 +235,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
|
|||
err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
icmph = skb_push(skb, sizeof(*icmph));
|
||||
*icmph = (struct icmphdr) {
|
||||
.type = ICMP_DEST_UNREACH,
|
||||
|
|
@ -281,7 +280,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
|
|||
*/
|
||||
static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
|
||||
{
|
||||
const struct icmphdr *icmph = icmp_hdr(skb);
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
|
||||
if (mtu < 576 || iph->frag_off != htons(IP_DF))
|
||||
|
|
@ -292,9 +290,17 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
|
|||
ipv4_is_lbcast(iph->saddr) || ipv4_is_multicast(iph->saddr))
|
||||
return 0;
|
||||
|
||||
if (iph->protocol == IPPROTO_ICMP && icmp_is_err(icmph->type))
|
||||
return 0;
|
||||
if (iph->protocol == IPPROTO_ICMP) {
|
||||
const struct icmphdr *icmph;
|
||||
|
||||
if (!pskb_network_may_pull(skb, iph->ihl * 4 +
|
||||
offsetofend(struct icmphdr, type)))
|
||||
return 0;
|
||||
iph = ip_hdr(skb);
|
||||
icmph = (void *)iph + iph->ihl * 4;
|
||||
if (icmp_is_err(icmph->type))
|
||||
return 0;
|
||||
}
|
||||
return iptunnel_pmtud_build_icmp(skb, mtu);
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +314,7 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
|
|||
*/
|
||||
static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
|
||||
{
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
||||
const struct ipv6hdr *ip6h;
|
||||
struct icmp6hdr *icmp6h;
|
||||
struct ipv6hdr *nip6h;
|
||||
struct ethhdr eh;
|
||||
|
|
@ -323,7 +329,6 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
|
|||
|
||||
skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);
|
||||
pskb_pull(skb, ETH_HLEN);
|
||||
skb_reset_network_header(skb);
|
||||
|
||||
err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h));
|
||||
if (err)
|
||||
|
|
@ -334,6 +339,7 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
ip6h = ipv6_hdr(skb);
|
||||
icmp6h = skb_push(skb, sizeof(*icmp6h));
|
||||
*icmp6h = (struct icmp6hdr) {
|
||||
.icmp6_type = ICMPV6_PKT_TOOBIG,
|
||||
|
|
|
|||
|
|
@ -1705,10 +1705,10 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net)
|
|||
{
|
||||
const struct ctl_table *table;
|
||||
|
||||
kfree(net->ipv4.sysctl_local_reserved_ports);
|
||||
table = net->ipv4.ipv4_hdr->ctl_table_arg;
|
||||
unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
|
||||
kfree(table);
|
||||
kfree(net->ipv4.sysctl_local_reserved_ports);
|
||||
}
|
||||
|
||||
static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
|
||||
|
|
|
|||
|
|
@ -617,6 +617,18 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
|
|||
}
|
||||
}
|
||||
|
||||
static u16 ipv6_get_exthdr_len(const struct sk_buff *skb, const u8 *ptr)
|
||||
{
|
||||
u16 len;
|
||||
|
||||
if (ptr + 2 > skb_tail_pointer(skb))
|
||||
return 0;
|
||||
|
||||
len = (ptr[1] + 1) << 3;
|
||||
|
||||
return (len <= skb_tail_pointer(skb) - ptr) ? len : 0;
|
||||
}
|
||||
|
||||
void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -643,7 +655,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
|
|||
/* HbH is allowed only once */
|
||||
if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
|
||||
u8 *ptr = nh + sizeof(struct ipv6hdr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
|
||||
u16 len = ipv6_get_exthdr_len(skb, ptr);
|
||||
|
||||
if (len)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, len, ptr);
|
||||
}
|
||||
|
||||
if (opt->lastopt &&
|
||||
|
|
@ -664,26 +679,37 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
|
|||
unsigned int len;
|
||||
u8 *ptr = nh + off;
|
||||
|
||||
if (ptr + 2 > skb_tail_pointer(skb))
|
||||
return;
|
||||
|
||||
switch (nexthdr) {
|
||||
case IPPROTO_DSTOPTS:
|
||||
nexthdr = ptr[0];
|
||||
len = (ptr[1] + 1) << 3;
|
||||
len = ipv6_get_exthdr_len(skb, ptr);
|
||||
if (!len)
|
||||
return;
|
||||
if (np->rxopt.bits.dstopts)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
|
||||
break;
|
||||
case IPPROTO_ROUTING:
|
||||
nexthdr = ptr[0];
|
||||
len = (ptr[1] + 1) << 3;
|
||||
len = ipv6_get_exthdr_len(skb, ptr);
|
||||
if (!len)
|
||||
return;
|
||||
if (np->rxopt.bits.srcrt)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
nexthdr = ptr[0];
|
||||
len = (ptr[1] + 2) << 2;
|
||||
if (ptr + len > skb_tail_pointer(skb))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
nexthdr = ptr[0];
|
||||
len = (ptr[1] + 1) << 3;
|
||||
len = ipv6_get_exthdr_len(skb, ptr);
|
||||
if (!len)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -705,19 +731,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
|
|||
}
|
||||
if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
|
||||
u8 *ptr = nh + sizeof(struct ipv6hdr);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
|
||||
u16 len = ipv6_get_exthdr_len(skb, ptr);
|
||||
|
||||
if (len)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, len, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.odstopts && opt->dst0) {
|
||||
u8 *ptr = nh + opt->dst0;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
u16 len = ipv6_get_exthdr_len(skb, ptr);
|
||||
|
||||
if (len)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.osrcrt && opt->srcrt) {
|
||||
struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
|
||||
u16 len = ipv6_get_exthdr_len(skb, (u8 *)rthdr);
|
||||
|
||||
if (len)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, len, rthdr);
|
||||
}
|
||||
if (np->rxopt.bits.odstopts && opt->dst1) {
|
||||
u8 *ptr = nh + opt->dst1;
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
|
||||
u16 len = ipv6_get_exthdr_len(skb, ptr);
|
||||
|
||||
if (len)
|
||||
put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
|
||||
}
|
||||
if (np->rxopt.bits.rxorigdstaddr) {
|
||||
struct sockaddr_in6 sin6;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ static bool ip6_parse_tlv(bool hopbyhop,
|
|||
case IPV6_TLV_JUMBO:
|
||||
if (!ipv6_hop_jumbo(skb, off))
|
||||
return false;
|
||||
|
||||
nh = skb_network_header(skb);
|
||||
break;
|
||||
case IPV6_TLV_CALIPSO:
|
||||
if (!ipv6_hop_calipso(skb, off))
|
||||
|
|
@ -201,6 +203,8 @@ static bool ip6_parse_tlv(bool hopbyhop,
|
|||
case IPV6_TLV_HAO:
|
||||
if (!ipv6_dest_hao(skb, off))
|
||||
return false;
|
||||
|
||||
nh = skb_network_header(skb);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
|
@ -544,7 +548,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
|
|||
* unsigned char which is segments_left field. Should not be
|
||||
* higher than that.
|
||||
*/
|
||||
if (r || (n + 1) > 255) {
|
||||
if (r || (n + 1) > 127) {
|
||||
kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -722,10 +722,11 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
|
|||
static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p,
|
||||
bool keep_mtu)
|
||||
{
|
||||
struct net *net = dev_net(t->dev);
|
||||
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
|
||||
struct net *net = t->net;
|
||||
struct vti6_net *ip6n;
|
||||
int err;
|
||||
|
||||
ip6n = net_generic(net, vti6_net_id);
|
||||
vti6_tnl_unlink(ip6n, t);
|
||||
synchronize_net();
|
||||
err = vti6_tnl_change(t, p, keep_mtu);
|
||||
|
|
@ -834,17 +835,24 @@ vti6_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data
|
|||
if (p.proto != IPPROTO_IPV6 && p.proto != 0)
|
||||
break;
|
||||
vti6_parm_from_user(&p1, &p);
|
||||
t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
|
||||
if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
|
||||
struct ip6_tnl *self = netdev_priv(dev);
|
||||
|
||||
err = -EPERM;
|
||||
if (!ns_capable(self->net->user_ns, CAP_NET_ADMIN))
|
||||
break;
|
||||
t = vti6_locate(self->net, &p1, false);
|
||||
if (t) {
|
||||
if (t->dev != dev) {
|
||||
err = -EEXIST;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
t = netdev_priv(dev);
|
||||
t = self;
|
||||
|
||||
err = vti6_update(t, &p1, false);
|
||||
} else {
|
||||
t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
|
||||
}
|
||||
if (t) {
|
||||
err = 0;
|
||||
|
|
@ -1031,11 +1039,12 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
|
|||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ip6_tnl *t;
|
||||
struct ip6_tnl *t = netdev_priv(dev);
|
||||
struct net *net = t->net;
|
||||
struct __ip6_tnl_parm p;
|
||||
struct net *net = dev_net(dev);
|
||||
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
|
||||
struct vti6_net *ip6n;
|
||||
|
||||
ip6n = net_generic(net, vti6_net_id);
|
||||
if (dev == ip6n->fb_tnl_dev)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -160,17 +160,33 @@ static bool nft_fib6_info_nh_dev_match(const struct net_device *nh_dev,
|
|||
l3mdev_master_ifindex_rcu(nh_dev) == dev->ifindex;
|
||||
}
|
||||
|
||||
static int nft_fib6_nh_match_dev_cb(struct fib6_nh *nh, void *arg)
|
||||
{
|
||||
const struct net_device *dev = arg;
|
||||
|
||||
return nft_fib6_info_nh_dev_match(nh->fib_nh_dev, dev);
|
||||
}
|
||||
|
||||
static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt,
|
||||
const struct net_device *dev)
|
||||
{
|
||||
const struct net_device *nh_dev;
|
||||
struct fib6_info *iter;
|
||||
|
||||
/* External nexthop: fib6_siblings slot aliases nh_list, walk via nh. */
|
||||
if (rt->nh)
|
||||
return nexthop_for_each_fib6_nh(rt->nh,
|
||||
nft_fib6_nh_match_dev_cb,
|
||||
(void *)dev);
|
||||
|
||||
nh_dev = fib6_info_nh_dev(rt);
|
||||
if (nft_fib6_info_nh_dev_match(nh_dev, dev))
|
||||
return true;
|
||||
|
||||
list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
|
||||
if (!READ_ONCE(rt->fib6_nsiblings))
|
||||
return false;
|
||||
|
||||
list_for_each_entry_rcu(iter, &rt->fib6_siblings, fib6_siblings) {
|
||||
nh_dev = fib6_info_nh_dev(iter);
|
||||
|
||||
if (nft_fib6_info_nh_dev_match(nh_dev, dev))
|
||||
|
|
|
|||
|
|
@ -1540,7 +1540,7 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
struct sock *sk = sock->sk;
|
||||
struct iucv_sock *iucv = iucv_sk(sk);
|
||||
unsigned int val;
|
||||
int len;
|
||||
int len, rc;
|
||||
|
||||
if (level != SOL_IUCV)
|
||||
return -ENOPROTOOPT;
|
||||
|
|
@ -1553,26 +1553,34 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
|
||||
len = min_t(unsigned int, len, sizeof(int));
|
||||
|
||||
rc = 0;
|
||||
|
||||
lock_sock(sk);
|
||||
switch (optname) {
|
||||
case SO_IPRMDATA_MSG:
|
||||
val = (iucv->flags & IUCV_IPRMDATA) ? 1 : 0;
|
||||
break;
|
||||
case SO_MSGLIMIT:
|
||||
lock_sock(sk);
|
||||
val = (iucv->path != NULL) ? iucv->path->msglim /* connected */
|
||||
: iucv->msglimit; /* default */
|
||||
release_sock(sk);
|
||||
break;
|
||||
case SO_MSGSIZE:
|
||||
if (sk->sk_state == IUCV_OPEN)
|
||||
return -EBADFD;
|
||||
if (sk->sk_state == IUCV_OPEN) {
|
||||
rc = -EBADFD;
|
||||
break;
|
||||
}
|
||||
val = (iucv->hs_dev) ? iucv->hs_dev->mtu -
|
||||
sizeof(struct af_iucv_trans_hdr) - ETH_HLEN :
|
||||
0x7fffffff;
|
||||
break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
rc = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
release_sock(sk);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (put_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
|
|
|||
|
|
@ -441,12 +441,13 @@ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
|
|||
idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
|
||||
if (tunnel) {
|
||||
list_for_each_entry_rcu(session, &tunnel->session_list, list) {
|
||||
if (!strcmp(session->ifname, ifname)) {
|
||||
refcount_inc(&session->ref_count);
|
||||
rcu_read_unlock_bh();
|
||||
if (strcmp(session->ifname, ifname))
|
||||
continue;
|
||||
if (!refcount_inc_not_zero(&session->ref_count))
|
||||
continue;
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
return session;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -568,6 +568,13 @@ static void destroy_gre_conntrack(struct nf_conn *ct)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void warn_on_keymap_list_leak(const struct net *net)
|
||||
{
|
||||
#ifdef CONFIG_NF_CT_PROTO_GRE
|
||||
WARN_ON_ONCE(!list_empty(&net->ct.nf_ct_proto.gre.keymap_list));
|
||||
#endif
|
||||
}
|
||||
|
||||
void nf_ct_destroy(struct nf_conntrack *nfct)
|
||||
{
|
||||
struct nf_conn *ct = (struct nf_conn *)nfct;
|
||||
|
|
@ -2510,6 +2517,7 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
|
|||
}
|
||||
|
||||
list_for_each_entry(net, net_exit_list, exit_list) {
|
||||
warn_on_keymap_list_leak(net);
|
||||
nf_conntrack_ecache_pernet_fini(net);
|
||||
nf_conntrack_expect_pernet_fini(net);
|
||||
free_percpu(net->ct.stat);
|
||||
|
|
|
|||
|
|
@ -225,13 +225,9 @@ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid)
|
|||
if (nf_ct_expect_related(exp_reply, 0) != 0)
|
||||
goto out_unexpect_orig;
|
||||
|
||||
/* Add GRE keymap entries */
|
||||
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0)
|
||||
if (!nf_ct_gre_keymap_add(ct, &exp_orig->tuple,
|
||||
&exp_reply->tuple))
|
||||
goto out_unexpect_both;
|
||||
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) {
|
||||
nf_ct_gre_keymap_destroy(ct);
|
||||
goto out_unexpect_both;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out_put_both:
|
||||
|
|
|
|||
|
|
@ -87,41 +87,97 @@ static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
|
|||
return key;
|
||||
}
|
||||
|
||||
/* add a single keymap entry, associate with specified master ct */
|
||||
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
|
||||
struct nf_conntrack_tuple *t)
|
||||
enum nf_ct_gre_km_act {
|
||||
NF_CT_GRE_KM_NEW,
|
||||
NF_CT_GRE_KM_BAD,
|
||||
NF_CT_GRE_KM_DUP
|
||||
};
|
||||
|
||||
static enum nf_ct_gre_km_act
|
||||
nf_ct_gre_km_acceptable(const struct nf_ct_pptp_master *ct_pptp_info,
|
||||
const struct nf_conntrack_tuple *orig,
|
||||
const struct nf_conntrack_tuple *repl)
|
||||
{
|
||||
struct nf_ct_gre_keymap *km_orig, *km_repl;
|
||||
|
||||
lockdep_assert_held(&keymap_lock);
|
||||
|
||||
km_orig = ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL];
|
||||
km_repl = ct_pptp_info->keymap[IP_CT_DIR_REPLY];
|
||||
|
||||
if (km_orig && km_repl) {
|
||||
if (!gre_key_cmpfn(km_orig, orig))
|
||||
return NF_CT_GRE_KM_BAD;
|
||||
|
||||
if (!gre_key_cmpfn(km_repl, repl))
|
||||
return NF_CT_GRE_KM_BAD;
|
||||
|
||||
return NF_CT_GRE_KM_DUP;
|
||||
}
|
||||
|
||||
DEBUG_NET_WARN_ON_ONCE(km_orig);
|
||||
DEBUG_NET_WARN_ON_ONCE(km_repl);
|
||||
return NF_CT_GRE_KM_NEW;
|
||||
}
|
||||
|
||||
/* add keymap entries, associate with specified master ct */
|
||||
bool nf_ct_gre_keymap_add(struct nf_conn *ct,
|
||||
const struct nf_conntrack_tuple *orig,
|
||||
const struct nf_conntrack_tuple *repl)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
struct nf_gre_net *net_gre = gre_pernet(net);
|
||||
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
|
||||
struct nf_ct_gre_keymap **kmp, *km;
|
||||
struct nf_ct_gre_keymap *km_orig, *km_repl;
|
||||
bool ret = false;
|
||||
|
||||
kmp = &ct_pptp_info->keymap[dir];
|
||||
if (*kmp) {
|
||||
/* check whether it's a retransmission */
|
||||
list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
|
||||
if (gre_key_cmpfn(km, t) && km == *kmp)
|
||||
return 0;
|
||||
}
|
||||
pr_debug("trying to override keymap_%s for ct %p\n",
|
||||
dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
|
||||
return -EEXIST;
|
||||
}
|
||||
km_orig = kmalloc_obj(*km_orig, GFP_ATOMIC);
|
||||
if (!km_orig)
|
||||
return false;
|
||||
km_repl = kmalloc_obj(*km_repl, GFP_ATOMIC);
|
||||
if (!km_repl)
|
||||
goto km_free;
|
||||
|
||||
km = kmalloc_obj(*km, GFP_ATOMIC);
|
||||
if (!km)
|
||||
return -ENOMEM;
|
||||
memcpy(&km->tuple, t, sizeof(*t));
|
||||
*kmp = km;
|
||||
|
||||
pr_debug("adding new entry %p: ", km);
|
||||
nf_ct_dump_tuple(&km->tuple);
|
||||
memcpy(&km_orig->tuple, orig, sizeof(*orig));
|
||||
memcpy(&km_repl->tuple, repl, sizeof(*repl));
|
||||
|
||||
spin_lock_bh(&keymap_lock);
|
||||
list_add_tail(&km->list, &net_gre->keymap_list);
|
||||
if (nf_ct_is_dying(ct))
|
||||
goto unlock_free;
|
||||
|
||||
switch (nf_ct_gre_km_acceptable(ct_pptp_info, orig, repl)) {
|
||||
case NF_CT_GRE_KM_NEW:
|
||||
break;
|
||||
case NF_CT_GRE_KM_DUP:
|
||||
ret = true;
|
||||
goto unlock_free;
|
||||
case NF_CT_GRE_KM_BAD:
|
||||
pr_debug("trying to override keymap for ct %p\n", ct);
|
||||
goto unlock_free;
|
||||
}
|
||||
|
||||
if (ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] ||
|
||||
ct_pptp_info->keymap[IP_CT_DIR_REPLY])
|
||||
goto unlock_free;
|
||||
|
||||
pr_debug("adding new entries %p,%p: ", km_orig, km_repl);
|
||||
nf_ct_dump_tuple(&km_orig->tuple);
|
||||
nf_ct_dump_tuple(&km_repl->tuple);
|
||||
|
||||
list_add_tail_rcu(&km_orig->list, &net_gre->keymap_list);
|
||||
list_add_tail_rcu(&km_repl->list, &net_gre->keymap_list);
|
||||
ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] = km_orig;
|
||||
ct_pptp_info->keymap[IP_CT_DIR_REPLY] = km_repl;
|
||||
spin_unlock_bh(&keymap_lock);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
unlock_free:
|
||||
spin_unlock_bh(&keymap_lock);
|
||||
km_free:
|
||||
kfree(km_orig);
|
||||
kfree(km_repl);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
|
||||
|
||||
|
|
|
|||
|
|
@ -1221,7 +1221,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
|
|||
new_state = old_state;
|
||||
}
|
||||
if (((test_bit(IPS_SEEN_REPLY_BIT, &ct->status)
|
||||
&& ct->proto.tcp.last_index == TCP_SYN_SET)
|
||||
&& ct->proto.tcp.last_index == TCP_SYN_SET
|
||||
&& ct->proto.tcp.last_dir != dir)
|
||||
|| (!test_bit(IPS_ASSURED_BIT, &ct->status)
|
||||
&& ct->proto.tcp.last_index == TCP_ACK_SET))
|
||||
&& ntohl(th->ack_seq) == ct->proto.tcp.last_end) {
|
||||
|
|
|
|||
|
|
@ -200,6 +200,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
|
|||
if (skb_ensure_writable(skb, optend))
|
||||
return 0;
|
||||
|
||||
th = (struct tcphdr *)(skb->data + protoff);
|
||||
|
||||
while (optoff < optend) {
|
||||
unsigned char *op = skb->data + optoff;
|
||||
|
||||
|
|
|
|||
|
|
@ -1141,6 +1141,9 @@ nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int di
|
|||
{
|
||||
struct sk_buff *nskb;
|
||||
|
||||
if (e->state.net->user_ns != &init_user_ns)
|
||||
return -EPERM;
|
||||
|
||||
if (diff < 0) {
|
||||
unsigned int min_len = skb_transport_offset(e->skb);
|
||||
|
||||
|
|
@ -1537,8 +1540,7 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
|
||||
payload_len, entry, diff) < 0)
|
||||
verdict = NF_DROP;
|
||||
|
||||
if (ct && diff)
|
||||
else if (ct && diff)
|
||||
nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ static void nft_bitwise_eval_lshift(u32 *dst, const u32 *src,
|
|||
u32 carry = 0;
|
||||
|
||||
for (i = DIV_ROUND_UP(priv->len, sizeof(u32)); i > 0; i--) {
|
||||
dst[i - 1] = (src[i - 1] << shift) | carry;
|
||||
carry = src[i - 1] >> (BITS_PER_TYPE(u32) - shift);
|
||||
u32 tmp_src = src[i - 1];
|
||||
|
||||
dst[i - 1] = (tmp_src << shift) | carry;
|
||||
carry = tmp_src >> (BITS_PER_TYPE(u32) - shift);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,8 +58,10 @@ static void nft_bitwise_eval_rshift(u32 *dst, const u32 *src,
|
|||
u32 carry = 0;
|
||||
|
||||
for (i = 0; i < DIV_ROUND_UP(priv->len, sizeof(u32)); i++) {
|
||||
dst[i] = carry | (src[i] >> shift);
|
||||
carry = src[i] << (BITS_PER_TYPE(u32) - shift);
|
||||
u32 tmp_src = src[i];
|
||||
|
||||
dst[i] = carry | (tmp_src >> shift);
|
||||
carry = tmp_src << (BITS_PER_TYPE(u32) - shift);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,6 +239,9 @@ static int nft_bitwise_init_bool(const struct nft_ctx *ctx,
|
|||
&priv->sreg2, priv->len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (nft_reg_overlap(priv->sreg2, priv->dreg, priv->len))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -265,6 +272,9 @@ static int nft_bitwise_init(const struct nft_ctx *ctx,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len))
|
||||
return -EINVAL;
|
||||
|
||||
if (tb[NFTA_BITWISE_OP]) {
|
||||
priv->op = ntohl(nla_get_be32(tb[NFTA_BITWISE_OP]));
|
||||
switch (priv->op) {
|
||||
|
|
|
|||
|
|
@ -144,9 +144,16 @@ static int nft_byteorder_init(const struct nft_ctx *ctx,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG],
|
||||
&priv->dreg, NULL, NFT_DATA_VALUE,
|
||||
priv->len);
|
||||
err = nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG],
|
||||
&priv->dreg, NULL, NFT_DATA_VALUE,
|
||||
priv->len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_byteorder_dump(struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -917,6 +917,9 @@ static int nft_payload_set_init(const struct nft_ctx *ctx,
|
|||
struct nft_payload_set *priv = nft_expr_priv(expr);
|
||||
int err;
|
||||
|
||||
if (ctx->net->user_ns != &init_user_ns)
|
||||
return -EPERM;
|
||||
|
||||
priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
|
||||
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ static bool cpu_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
{
|
||||
const struct xt_cpu_info *info = par->matchinfo;
|
||||
|
||||
return (info->cpu == smp_processor_id()) ^ info->invert;
|
||||
return (info->cpu == raw_smp_processor_id()) ^ info->invert;
|
||||
}
|
||||
|
||||
static struct xt_match cpu_mt_reg __read_mostly = {
|
||||
|
|
|
|||
|
|
@ -1482,9 +1482,14 @@ static void do_one_broadcast(struct sock *sk,
|
|||
p->skb2 = NULL;
|
||||
goto out;
|
||||
}
|
||||
NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net);
|
||||
if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED)
|
||||
NETLINK_CB(p->skb2).nsid_is_set = true;
|
||||
|
||||
NETLINK_CB(p->skb2).nsid_is_set = false;
|
||||
if (!net_eq(sock_net(sk), p->net)) {
|
||||
NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net);
|
||||
if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED)
|
||||
NETLINK_CB(p->skb2).nsid_is_set = true;
|
||||
}
|
||||
|
||||
val = netlink_broadcast_deliver(sk, p->skb2);
|
||||
if (val < 0) {
|
||||
netlink_overrun(sk);
|
||||
|
|
|
|||
|
|
@ -861,6 +861,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|||
struct sk_buff *frag_skb;
|
||||
int msg_len;
|
||||
|
||||
if (!pskb_may_pull(skb, NFC_HCI_HCP_PACKET_HEADER_LEN)) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
packet = (struct hcp_packet *)skb->data;
|
||||
if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
|
||||
skb_queue_tail(&hdev->rx_hcp_frags, skb);
|
||||
|
|
@ -904,6 +909,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|||
* unblock waiting cmd context. Otherwise, enqueue to dispatch
|
||||
* in separate context where handler can also execute command.
|
||||
*/
|
||||
if (!pskb_may_pull(hcp_skb, NFC_HCI_HCP_HEADER_LEN)) {
|
||||
kfree_skb(hcp_skb);
|
||||
return;
|
||||
}
|
||||
|
||||
packet = (struct hcp_packet *)hcp_skb->data;
|
||||
type = HCP_MSG_GET_TYPE(packet->message.header);
|
||||
if (type == NFC_HCI_HCP_RESPONSE) {
|
||||
|
|
|
|||
|
|
@ -1218,6 +1218,15 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local,
|
|||
|
||||
sk = &llcp_sock->sk;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
/* Check if socket was destroyed whilst waiting for the lock */
|
||||
if (!sk_hashed(sk)) {
|
||||
release_sock(sk);
|
||||
nfc_llcp_sock_put(llcp_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unlink from connecting and link to the client array */
|
||||
nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
|
||||
nfc_llcp_sock_link(&local->sockets, sk);
|
||||
|
|
@ -1229,6 +1238,8 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local,
|
|||
sk->sk_state = LLCP_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
nfc_llcp_sock_put(llcp_sock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -633,6 +633,8 @@ static int llcp_sock_release(struct socket *sock)
|
|||
|
||||
if (sock->type == SOCK_RAW)
|
||||
nfc_llcp_sock_unlink(&local->raw_sockets, sk);
|
||||
else if (sk->sk_state == LLCP_CONNECTING)
|
||||
nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
|
||||
else
|
||||
nfc_llcp_sock_unlink(&local->sockets, sk);
|
||||
|
||||
|
|
|
|||
|
|
@ -439,6 +439,11 @@ void nci_hci_data_received_cb(void *context,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN)) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
packet = (struct nci_hcp_packet *)skb->data;
|
||||
if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) {
|
||||
skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
|
||||
|
|
@ -482,6 +487,11 @@ void nci_hci_data_received_cb(void *context,
|
|||
* unblock waiting cmd context. Otherwise, enqueue to dispatch
|
||||
* in separate context where handler can also execute command.
|
||||
*/
|
||||
if (!pskb_may_pull(hcp_skb, NCI_HCI_HCP_HEADER_LEN)) {
|
||||
kfree_skb(hcp_skb);
|
||||
return;
|
||||
}
|
||||
|
||||
packet = (struct nci_hcp_packet *)hcp_skb->data;
|
||||
type = NCI_HCP_MSG_GET_TYPE(packet->message.header);
|
||||
if (type == NCI_HCI_HCP_RESPONSE) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
#include <net/tc_act/tc_mirred.h>
|
||||
#include <net/tc_wrapper.h>
|
||||
|
||||
#define MIRRED_DEFER_LIMIT 3
|
||||
_Static_assert(MIRRED_DEFER_LIMIT <= 3,
|
||||
"MIRRED_DEFER_LIMIT exceeds tc_depth bitfield width");
|
||||
|
||||
static LIST_HEAD(mirred_list);
|
||||
static DEFINE_SPINLOCK(mirred_list_lock);
|
||||
|
||||
|
|
@ -234,12 +238,15 @@ tcf_mirred_forward(bool at_ingress, bool want_ingress, struct sk_buff *skb)
|
|||
{
|
||||
int err;
|
||||
|
||||
if (!want_ingress)
|
||||
if (!want_ingress) {
|
||||
err = tcf_dev_queue_xmit(skb, dev_queue_xmit);
|
||||
else if (!at_ingress)
|
||||
err = netif_rx(skb);
|
||||
else
|
||||
err = netif_receive_skb(skb);
|
||||
} else {
|
||||
skb->tc_depth++;
|
||||
if (!at_ingress)
|
||||
err = netif_rx(skb);
|
||||
else
|
||||
err = netif_receive_skb(skb);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -365,7 +372,8 @@ static int tcf_blockcast_redir(struct sk_buff *skb, struct tcf_mirred *m,
|
|||
dev_is_mac_header_xmit(dev_prev),
|
||||
m_eaction, retval);
|
||||
|
||||
return retval;
|
||||
/* If the packet wasn't redirected, we have to register as a drop */
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
|
||||
|
|
@ -389,14 +397,12 @@ static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
|
|||
|
||||
static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
|
||||
const u32 blockid, struct tcf_result *res,
|
||||
int retval)
|
||||
int m_eaction, int retval)
|
||||
{
|
||||
const u32 exception_ifindex = skb->dev->ifindex;
|
||||
struct tcf_block *block;
|
||||
bool is_redirect;
|
||||
int m_eaction;
|
||||
|
||||
m_eaction = READ_ONCE(m->tcfm_eaction);
|
||||
is_redirect = tcf_mirred_is_act_redirect(m_eaction);
|
||||
|
||||
/* we are already under rcu protection, so can call block lookup
|
||||
|
|
@ -405,7 +411,7 @@ static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
|
|||
block = tcf_block_lookup(dev_net(skb->dev), blockid);
|
||||
if (!block || xa_empty(&block->ports)) {
|
||||
tcf_action_inc_overlimit_qstats(&m->common);
|
||||
return retval;
|
||||
return is_redirect ? TC_ACT_SHOT : retval;
|
||||
}
|
||||
|
||||
if (is_redirect)
|
||||
|
|
@ -423,9 +429,10 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
|
|||
{
|
||||
struct tcf_mirred *m = to_mirred(a);
|
||||
int retval = READ_ONCE(m->tcf_action);
|
||||
bool m_mac_header_xmit, is_redirect;
|
||||
struct netdev_xmit *xmit;
|
||||
bool m_mac_header_xmit;
|
||||
struct net_device *dev;
|
||||
bool want_ingress;
|
||||
int i, m_eaction;
|
||||
u32 blockid;
|
||||
|
||||
|
|
@ -434,7 +441,8 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
|
|||
#else
|
||||
xmit = this_cpu_ptr(&softnet_data.xmit);
|
||||
#endif
|
||||
if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT)) {
|
||||
if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT ||
|
||||
skb->tc_depth >= MIRRED_DEFER_LIMIT)) {
|
||||
net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
|
||||
netdev_name(skb->dev));
|
||||
return TC_ACT_SHOT;
|
||||
|
|
@ -444,34 +452,51 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
|
|||
tcf_action_update_bstats(&m->common, skb);
|
||||
|
||||
blockid = READ_ONCE(m->tcfm_blockid);
|
||||
if (blockid)
|
||||
return tcf_blockcast(skb, m, blockid, res, retval);
|
||||
m_eaction = READ_ONCE(m->tcfm_eaction);
|
||||
want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
|
||||
if (blockid) {
|
||||
if (!want_ingress)
|
||||
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = NULL;
|
||||
retval = tcf_blockcast(skb, m, blockid, res, m_eaction, retval);
|
||||
if (!want_ingress)
|
||||
xmit->sched_mirred_nest--;
|
||||
return retval;
|
||||
}
|
||||
|
||||
is_redirect = tcf_mirred_is_act_redirect(m_eaction);
|
||||
|
||||
dev = rcu_dereference_bh(m->tcfm_dev);
|
||||
if (unlikely(!dev)) {
|
||||
pr_notice_once("tc mirred: target device is gone\n");
|
||||
tcf_action_inc_overlimit_qstats(&m->common);
|
||||
return retval;
|
||||
}
|
||||
for (i = 0; i < xmit->sched_mirred_nest; i++) {
|
||||
if (xmit->sched_mirred_dev[i] != dev)
|
||||
continue;
|
||||
pr_notice_once("tc mirred: loop on device %s\n",
|
||||
netdev_name(dev));
|
||||
tcf_action_inc_overlimit_qstats(&m->common);
|
||||
return retval;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
|
||||
if (!want_ingress) {
|
||||
for (i = 0; i < xmit->sched_mirred_nest; i++) {
|
||||
if (xmit->sched_mirred_dev[i] != dev)
|
||||
continue;
|
||||
pr_notice_once("tc mirred: loop on device %s\n",
|
||||
netdev_name(dev));
|
||||
tcf_action_inc_overlimit_qstats(&m->common);
|
||||
goto err_out;
|
||||
}
|
||||
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
|
||||
}
|
||||
|
||||
m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
|
||||
m_eaction = READ_ONCE(m->tcfm_eaction);
|
||||
|
||||
retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction,
|
||||
retval);
|
||||
xmit->sched_mirred_nest--;
|
||||
if (!want_ingress)
|
||||
xmit->sched_mirred_nest--;
|
||||
|
||||
return retval;
|
||||
|
||||
err_out:
|
||||
if (is_redirect)
|
||||
retval = TC_ACT_SHOT;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets,
|
||||
|
|
|
|||
|
|
@ -461,7 +461,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
skb->prev = NULL;
|
||||
|
||||
/* Random duplication */
|
||||
if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
|
||||
if (q->duplicate && skb->tc_depth == 0 &&
|
||||
q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
|
||||
++count;
|
||||
|
||||
/* Drop packet? */
|
||||
|
|
@ -540,11 +541,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
*/
|
||||
if (skb2) {
|
||||
struct Qdisc *rootq = qdisc_root_bh(sch);
|
||||
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
|
||||
|
||||
q->duplicate = 0;
|
||||
skb2->tc_depth++; /* prevent duplicating a dup... */
|
||||
rootq->enqueue(skb2, rootq, to_free);
|
||||
q->duplicate = dupsave;
|
||||
skb2 = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1007,41 +1006,6 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct Qdisc_class_ops netem_class_ops;
|
||||
|
||||
static int check_netem_in_tree(struct Qdisc *sch, bool duplicates,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct Qdisc *root, *q;
|
||||
unsigned int i;
|
||||
|
||||
root = qdisc_root_sleeping(sch);
|
||||
|
||||
if (sch != root && root->ops->cl_ops == &netem_class_ops) {
|
||||
if (duplicates ||
|
||||
((struct netem_sched_data *)qdisc_priv(root))->duplicate)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!qdisc_dev(root))
|
||||
return 0;
|
||||
|
||||
hash_for_each(qdisc_dev(root)->qdisc_hash, i, q, hash) {
|
||||
if (sch != q && q->ops->cl_ops == &netem_class_ops) {
|
||||
if (duplicates ||
|
||||
((struct netem_sched_data *)qdisc_priv(q))->duplicate)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"netem: cannot mix duplicating netems with other netems in tree");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse netlink message to set options */
|
||||
static int netem_change(struct Qdisc *sch, struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
|
@ -1118,11 +1082,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
|
|||
q->gap = qopt->gap;
|
||||
q->counter = 0;
|
||||
q->loss = qopt->loss;
|
||||
|
||||
ret = check_netem_in_tree(sch, qopt->duplicate, extack);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
q->duplicate = qopt->duplicate;
|
||||
|
||||
/* for compatibility with earlier versions.
|
||||
|
|
|
|||
|
|
@ -188,10 +188,12 @@ static bool smc_hs_congested(const struct sock *sk)
|
|||
|
||||
struct smc_hashinfo smc_v4_hashinfo = {
|
||||
.lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock),
|
||||
.ht = HLIST_HEAD_INIT,
|
||||
};
|
||||
|
||||
struct smc_hashinfo smc_v6_hashinfo = {
|
||||
.lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock),
|
||||
.ht = HLIST_HEAD_INIT,
|
||||
};
|
||||
|
||||
int smc_hash_sk(struct sock *sk)
|
||||
|
|
@ -3517,8 +3519,6 @@ static int __init smc_init(void)
|
|||
pr_err("%s: sock_register fails with %d\n", __func__, rc);
|
||||
goto out_proto6;
|
||||
}
|
||||
INIT_HLIST_HEAD(&smc_v4_hashinfo.ht);
|
||||
INIT_HLIST_HEAD(&smc_v6_hashinfo.ht);
|
||||
|
||||
rc = smc_ib_register_client();
|
||||
if (rc) {
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
|
|||
*/
|
||||
sock_reset_flag(sk, SOCK_DONE);
|
||||
sk->sk_state = TCP_CLOSE;
|
||||
vsk->peer_shutdown = 0;
|
||||
WRITE_ONCE(vsk->peer_shutdown, 0);
|
||||
}
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
|
|
@ -933,7 +933,7 @@ static struct sock *__vsock_create(struct net *net,
|
|||
vsk->rejected = false;
|
||||
vsk->sent_request = false;
|
||||
vsk->ignore_connecting_rst = false;
|
||||
vsk->peer_shutdown = 0;
|
||||
WRITE_ONCE(vsk->peer_shutdown, 0);
|
||||
INIT_DELAYED_WORK(&vsk->connect_work, vsock_connect_timeout);
|
||||
INIT_DELAYED_WORK(&vsk->pending_work, vsock_pending_work);
|
||||
|
||||
|
|
@ -1241,6 +1241,25 @@ static int vsock_shutdown(struct socket *sock, int mode)
|
|||
return err;
|
||||
}
|
||||
|
||||
static __poll_t vsock_poll_shutdown(struct sock *sk, u32 peer_shutdown)
|
||||
{
|
||||
__poll_t mask = 0;
|
||||
|
||||
/* INET sockets treat local write shutdown and peer write shutdown as a
|
||||
* case of EPOLLHUP set.
|
||||
*/
|
||||
if (sk->sk_shutdown == SHUTDOWN_MASK ||
|
||||
((sk->sk_shutdown & SEND_SHUTDOWN) &&
|
||||
(peer_shutdown & SEND_SHUTDOWN)))
|
||||
mask |= EPOLLHUP;
|
||||
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN ||
|
||||
peer_shutdown & SEND_SHUTDOWN)
|
||||
mask |= EPOLLRDHUP;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static __poll_t vsock_poll(struct file *file, struct socket *sock,
|
||||
poll_table *wait)
|
||||
{
|
||||
|
|
@ -1258,24 +1277,17 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
|
|||
/* Signify that there has been an error on this socket. */
|
||||
mask |= EPOLLERR;
|
||||
|
||||
/* INET sockets treat local write shutdown and peer write shutdown as a
|
||||
* case of EPOLLHUP set.
|
||||
*/
|
||||
if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
|
||||
((sk->sk_shutdown & SEND_SHUTDOWN) &&
|
||||
(vsk->peer_shutdown & SEND_SHUTDOWN))) {
|
||||
mask |= EPOLLHUP;
|
||||
}
|
||||
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN ||
|
||||
vsk->peer_shutdown & SEND_SHUTDOWN) {
|
||||
mask |= EPOLLRDHUP;
|
||||
}
|
||||
|
||||
if (sk_is_readable(sk))
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
|
||||
if (sock->type == SOCK_DGRAM) {
|
||||
u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown);
|
||||
|
||||
/* DGRAM sockets do not take lock_sock() in poll(), so use one
|
||||
* lockless snapshot for all shutdown-derived mask bits.
|
||||
*/
|
||||
mask |= vsock_poll_shutdown(sk, peer_shutdown);
|
||||
|
||||
/* For datagram sockets we can read if there is something in
|
||||
* the queue and write as long as the socket isn't shutdown for
|
||||
* sending.
|
||||
|
|
@ -1290,6 +1302,7 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
|
|||
|
||||
} else if (sock_type_connectible(sk->sk_type)) {
|
||||
const struct vsock_transport *transport;
|
||||
u32 peer_shutdown;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
|
|
@ -1322,8 +1335,10 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
|
|||
* terminated should also be considered read, and we check the
|
||||
* shutdown flag for that.
|
||||
*/
|
||||
peer_shutdown = READ_ONCE(vsk->peer_shutdown);
|
||||
mask |= vsock_poll_shutdown(sk, peer_shutdown);
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN ||
|
||||
vsk->peer_shutdown & SEND_SHUTDOWN) {
|
||||
peer_shutdown & SEND_SHUTDOWN) {
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ static void hvs_do_close_lock_held(struct vsock_sock *vsk,
|
|||
struct sock *sk = sk_vsock(vsk);
|
||||
|
||||
sock_set_flag(sk, SOCK_DONE);
|
||||
vsk->peer_shutdown = SHUTDOWN_MASK;
|
||||
WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
|
||||
if (vsock_stream_has_data(vsk) <= 0)
|
||||
sk->sk_state = TCP_CLOSING;
|
||||
sk->sk_state_change(sk);
|
||||
|
|
@ -593,7 +593,9 @@ static int hvs_update_recv_data(struct hvsock *hvs)
|
|||
return -EIO;
|
||||
|
||||
if (payload_len == 0)
|
||||
hvs->vsk->peer_shutdown |= SEND_SHUTDOWN;
|
||||
WRITE_ONCE(hvs->vsk->peer_shutdown,
|
||||
READ_ONCE(hvs->vsk->peer_shutdown) |
|
||||
SEND_SHUTDOWN);
|
||||
|
||||
hvs->recv_data_len = payload_len;
|
||||
hvs->recv_data_off = 0;
|
||||
|
|
@ -736,7 +738,8 @@ static s64 hvs_stream_has_data(struct vsock_sock *vsk)
|
|||
return ret;
|
||||
return hvs->recv_data_len;
|
||||
case 0:
|
||||
vsk->peer_shutdown |= SEND_SHUTDOWN;
|
||||
WRITE_ONCE(vsk->peer_shutdown,
|
||||
READ_ONCE(vsk->peer_shutdown) | SEND_SHUTDOWN);
|
||||
ret = 0;
|
||||
break;
|
||||
default: /* -1 */
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
|
|||
static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
|
||||
u32 len)
|
||||
{
|
||||
u64 skb_overhead = (skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0);
|
||||
u64 skb_overhead = ((u64)skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0);
|
||||
|
||||
/* Allow at most buf_alloc * 2 total budget (payload + overhead),
|
||||
* similar to how SO_RCVBUF is doubled to reserve space for sk_buff
|
||||
|
|
@ -1228,7 +1228,7 @@ static void virtio_transport_do_close(struct vsock_sock *vsk,
|
|||
struct sock *sk = sk_vsock(vsk);
|
||||
|
||||
sock_set_flag(sk, SOCK_DONE);
|
||||
vsk->peer_shutdown = SHUTDOWN_MASK;
|
||||
WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
|
||||
if (vsock_stream_has_data(vsk) <= 0)
|
||||
sk->sk_state = TCP_CLOSING;
|
||||
sk->sk_state_change(sk);
|
||||
|
|
@ -1431,12 +1431,15 @@ virtio_transport_recv_connected(struct sock *sk,
|
|||
case VIRTIO_VSOCK_OP_CREDIT_UPDATE:
|
||||
sk->sk_write_space(sk);
|
||||
break;
|
||||
case VIRTIO_VSOCK_OP_SHUTDOWN:
|
||||
case VIRTIO_VSOCK_OP_SHUTDOWN: {
|
||||
u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown);
|
||||
|
||||
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_RCV)
|
||||
vsk->peer_shutdown |= RCV_SHUTDOWN;
|
||||
peer_shutdown |= RCV_SHUTDOWN;
|
||||
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
|
||||
vsk->peer_shutdown |= SEND_SHUTDOWN;
|
||||
if (vsk->peer_shutdown == SHUTDOWN_MASK) {
|
||||
peer_shutdown |= SEND_SHUTDOWN;
|
||||
WRITE_ONCE(vsk->peer_shutdown, peer_shutdown);
|
||||
if (peer_shutdown == SHUTDOWN_MASK) {
|
||||
if (vsock_stream_has_data(vsk) <= 0 && !sock_flag(sk, SOCK_DONE)) {
|
||||
(void)virtio_transport_reset(vsk, NULL);
|
||||
virtio_transport_do_close(vsk, true);
|
||||
|
|
@ -1451,6 +1454,7 @@ virtio_transport_recv_connected(struct sock *sk,
|
|||
if (le32_to_cpu(virtio_vsock_hdr(skb)->flags))
|
||||
sk->sk_state_change(sk);
|
||||
break;
|
||||
}
|
||||
case VIRTIO_VSOCK_OP_RST:
|
||||
virtio_transport_do_close(vsk, true);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -819,7 +819,7 @@ static void vmci_transport_handle_detach(struct sock *sk)
|
|||
/* On a detach the peer will not be sending or receiving
|
||||
* anymore.
|
||||
*/
|
||||
vsk->peer_shutdown = SHUTDOWN_MASK;
|
||||
WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
|
||||
|
||||
/* We should not be sending anymore since the peer won't be
|
||||
* there to receive, but we can still receive if there is data
|
||||
|
|
@ -1542,7 +1542,9 @@ static int vmci_transport_recv_connected(struct sock *sk,
|
|||
if (pkt->u.mode) {
|
||||
vsk = vsock_sk(sk);
|
||||
|
||||
vsk->peer_shutdown |= pkt->u.mode;
|
||||
WRITE_ONCE(vsk->peer_shutdown,
|
||||
READ_ONCE(vsk->peer_shutdown) |
|
||||
pkt->u.mode);
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1559,7 +1561,7 @@ static int vmci_transport_recv_connected(struct sock *sk,
|
|||
* a clean shutdown.
|
||||
*/
|
||||
sock_set_flag(sk, SOCK_DONE);
|
||||
vsk->peer_shutdown = SHUTDOWN_MASK;
|
||||
WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
|
||||
if (vsock_stream_has_data(vsk) <= 0)
|
||||
sk->sk_state = TCP_CLOSING;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
|
||||
import time
|
||||
|
||||
from lib.py import ksft_run, ksft_exit, ksft_true
|
||||
from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_true
|
||||
from lib.py import ip
|
||||
from lib.py import NetNS, NetNSEnter
|
||||
from lib.py import RtnlFamily
|
||||
|
||||
|
||||
LINK_NETNSID = 100
|
||||
LINK_NETNSID2 = 200
|
||||
|
||||
|
||||
def test_event() -> None:
|
||||
|
|
@ -32,6 +33,57 @@ def test_event() -> None:
|
|||
"Received unexpected link notification")
|
||||
|
||||
|
||||
def test_event_all_nsid() -> None:
|
||||
"""NETLINK_LISTEN_ALL_NSID notifications: local events must not
|
||||
carry nsid even with a self-referential mapping. Remote events
|
||||
must carry the correct nsid."""
|
||||
|
||||
with NetNS() as ns1, NetNS() as ns2:
|
||||
net1, net2 = str(ns1), str(ns2)
|
||||
|
||||
with NetNSEnter(net1):
|
||||
rtnl = RtnlFamily()
|
||||
rtnl.ntf_listen_all_nsid()
|
||||
rtnl.ntf_subscribe("rtnlgrp-link")
|
||||
|
||||
# Case 1: no nsid assigned, local event, no nsid expected.
|
||||
ip("link add dummy-lo type dummy", ns=net1)
|
||||
|
||||
# Case 2: self-referential nsid, local event, still no nsid.
|
||||
ip(f"netns set {net1} {LINK_NETNSID}", ns=net1)
|
||||
ip("link add dummy-sr type dummy", ns=net1)
|
||||
|
||||
# Case 3: remote event, nsid present.
|
||||
ip(f"netns set {net2} {LINK_NETNSID2}", ns=net1)
|
||||
ip("link add dummy-re type dummy", ns=net2)
|
||||
|
||||
# Collect the three newlink events, ignoring unrelated noise.
|
||||
events = {}
|
||||
for msg in rtnl.poll_ntf(duration=1):
|
||||
if msg['name'] == 'getlink':
|
||||
ifname = msg['msg'].get('ifname')
|
||||
if ifname in ('dummy-lo', 'dummy-sr', 'dummy-re'):
|
||||
events[ifname] = msg
|
||||
if len(events) == 3:
|
||||
break
|
||||
|
||||
ksft_true('dummy-lo' in events, "missing local event")
|
||||
ksft_true(events['dummy-lo'].get('nsid') is None,
|
||||
"local event without nsid should not carry nsid")
|
||||
|
||||
ksft_true('dummy-sr' in events, "missing self-ref event")
|
||||
ksft_true(events['dummy-sr'].get('nsid') is None,
|
||||
"local event with self-ref nsid should not carry nsid")
|
||||
|
||||
ksft_true('dummy-re' in events, "missing remote event")
|
||||
ksft_eq(events['dummy-re'].get('nsid'), LINK_NETNSID2,
|
||||
"remote event should carry nsid")
|
||||
|
||||
ip("link del dummy-lo", ns=net1)
|
||||
ip("link del dummy-sr", ns=net1)
|
||||
ip("link del dummy-re", ns=net2)
|
||||
|
||||
|
||||
def validate_link_netns(netns, ifname, link_netnsid) -> bool:
|
||||
link_info = ip(f"-d link show dev {ifname}", ns=netns, json=True)
|
||||
if not link_info:
|
||||
|
|
@ -133,7 +185,12 @@ def test_peer_net() -> None:
|
|||
|
||||
|
||||
def main() -> None:
|
||||
ksft_run([test_event, test_link_net, test_peer_net])
|
||||
ksft_run([
|
||||
test_event,
|
||||
test_event_all_nsid,
|
||||
test_link_net,
|
||||
test_peer_net,
|
||||
])
|
||||
ksft_exit()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ TEST_PROGS := \
|
|||
nft_concat_range.sh \
|
||||
nft_conntrack_helper.sh \
|
||||
nft_fib.sh \
|
||||
nft_fib_nexthop.sh \
|
||||
nft_flowtable.sh \
|
||||
nft_interface_stress.sh \
|
||||
nft_meta.sh \
|
||||
|
|
|
|||
152
tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh
Executable file
152
tools/testing/selftests/net/netfilter/nft_fib_nexthop.sh
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Exercise nft_fib6_eval()'s sibling/nh enumeration on three route shapes:
|
||||
# 1) route via a single external nexthop (nhid)
|
||||
# 2) route via an external nexthop group (nhid -> group, two members)
|
||||
# 3) route via old-style multipath (nexthop ... nexthop ...)
|
||||
#
|
||||
# In each scenario the route's nexthop set contains veth0 (the iif of the
|
||||
# test packet). nft_fib6_info_nh_uses_dev() must walk the set and report
|
||||
# veth0 as a valid oif. For (2) and (3) the matching nexthop is the second
|
||||
# member, so the walk has to traverse beyond the primary nh.
|
||||
#
|
||||
# After sending $PKTS ICMPv6 echo requests from ns1, check two counters on
|
||||
# nsrouter:
|
||||
# nf_ok -- `fib daddr . iif oif eq "veth0"` must equal $PKTS
|
||||
# nf_bad -- `fib daddr . iif oif missing` must stay at 0
|
||||
# Both rules also match on iif veth0 and ip6 daddr dead:dead::/64 so that
|
||||
# kernel-generated ND/MLD/RA traffic cannot pollute the counters.
|
||||
#
|
||||
# Topology similar to nft_fib.sh, without ns2; two dummy interfaces on
|
||||
# nsrouter host extra nh devices:
|
||||
#
|
||||
# dead:1::99 dead:1::1
|
||||
# ns1 <----veth----> nsrouter --- dummy0 dead:2::1
|
||||
# \-- dummy1 dead:9::1
|
||||
|
||||
source lib.sh
|
||||
|
||||
ret=0
|
||||
PKTS=3
|
||||
|
||||
checktool "nft --version" "run test without nft"
|
||||
checktool "ip -V" "run test without iproute2"
|
||||
|
||||
setup_ns nsrouter ns1
|
||||
trap cleanup_all_ns EXIT
|
||||
|
||||
if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" \
|
||||
> /dev/null 2>&1; then
|
||||
echo "SKIP: No virtual ethernet pair device support in kernel"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
ip -net "$ns1" link set lo up
|
||||
ip -net "$ns1" link set eth0 up
|
||||
ip -net "$ns1" -6 addr add dead:1::99/64 dev eth0 nodad
|
||||
ip -net "$ns1" -6 route add default via dead:1::1
|
||||
|
||||
ip -net "$nsrouter" link set lo up
|
||||
ip -net "$nsrouter" link set veth0 up
|
||||
ip -net "$nsrouter" -6 addr add dead:1::1/64 dev veth0 nodad
|
||||
|
||||
if ! ip -net "$nsrouter" link add dummy0 type dummy 2>/dev/null; then
|
||||
echo "SKIP: dummy netdev not available"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
ip -net "$nsrouter" link set dummy0 up
|
||||
ip -net "$nsrouter" -6 addr add dead:2::1/64 dev dummy0 nodad
|
||||
|
||||
ip -net "$nsrouter" link add dummy1 type dummy
|
||||
ip -net "$nsrouter" link set dummy1 up
|
||||
ip -net "$nsrouter" -6 addr add dead:9::1/64 dev dummy1 nodad
|
||||
|
||||
ip netns exec "$nsrouter" sysctl -q net.ipv6.conf.all.forwarding=1
|
||||
|
||||
load_fib_rule() {
|
||||
# filter on iif + daddr so the counters only see our test packets
|
||||
ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
|
||||
flush ruleset
|
||||
table ip6 t {
|
||||
counter nf_ok { }
|
||||
counter nf_bad { }
|
||||
chain c {
|
||||
type filter hook prerouting priority 0; policy accept;
|
||||
iif "veth0" ip6 daddr dead:dead::/64 fib daddr . iif oif eq "veth0" counter name nf_ok
|
||||
iif "veth0" ip6 daddr dead:dead::/64 fib daddr . iif oif missing counter name nf_bad
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
bad_counter() {
|
||||
local counter=$1
|
||||
local expect=$2
|
||||
local tag=$3
|
||||
|
||||
echo "FAIL ($tag): counter $counter has unexpected value (expected \"$expect\")" 1>&2
|
||||
ip netns exec "$nsrouter" nft list counter ip6 t "$counter" 1>&2
|
||||
}
|
||||
|
||||
run_scenario() {
|
||||
local what="$1"; shift
|
||||
# counter output format is "packets PACKET_NUM bytes BYTES_NUM";
|
||||
# we only care about the packet count
|
||||
local expect_ok="packets $PKTS bytes"
|
||||
local expect_bad="packets 0 bytes"
|
||||
local lret=0
|
||||
|
||||
# reset route + nexthop state between scenarios
|
||||
ip -net "$nsrouter" -6 route del dead:dead::/64 > /dev/null 2>&1 || true
|
||||
ip -net "$nsrouter" nexthop flush > /dev/null 2>&1 || true
|
||||
|
||||
# run the scenario function passed by the caller
|
||||
"$@" || echo "WARN ($what): scenario setup returned non-zero"
|
||||
|
||||
load_fib_rule || { echo "FAIL ($what): nft load"; ret=1; return; }
|
||||
|
||||
# ping a daddr inside dead:dead::/64 so fib has to walk the nh set
|
||||
ip netns exec "$ns1" ping -6 -c "$PKTS" -i 0.1 -W 1 dead:dead::1 \
|
||||
> /dev/null 2>&1 || true
|
||||
|
||||
# verify the packets went through the expected fib path
|
||||
if ! ip netns exec "$nsrouter" nft list counter ip6 t nf_ok | grep -q "$expect_ok"; then
|
||||
bad_counter nf_ok "$expect_ok" "$what"
|
||||
lret=1
|
||||
fi
|
||||
if ! ip netns exec "$nsrouter" nft list counter ip6 t nf_bad | grep -q "$expect_bad"; then
|
||||
bad_counter nf_bad "$expect_bad" "$what"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
if [ $lret -eq 0 ]; then
|
||||
echo "PASS: $what"
|
||||
else
|
||||
ret=1
|
||||
fi
|
||||
}
|
||||
|
||||
scenario_single_nh() {
|
||||
ip -net "$nsrouter" nexthop add id 1 via dead:1::99 dev veth0
|
||||
ip -net "$nsrouter" -6 route add dead:dead::/64 nhid 1
|
||||
}
|
||||
run_scenario "single external nexthop (nhid -> veth0)" scenario_single_nh
|
||||
|
||||
scenario_nh_group() {
|
||||
ip -net "$nsrouter" nexthop add id 1 via dead:2::2 dev dummy0
|
||||
ip -net "$nsrouter" nexthop add id 2 via dead:1::99 dev veth0
|
||||
ip -net "$nsrouter" nexthop add id 100 group 1/2
|
||||
ip -net "$nsrouter" -6 route add dead:dead::/64 nhid 100
|
||||
}
|
||||
run_scenario "nexthop group (dummy0 + veth0)" scenario_nh_group
|
||||
|
||||
scenario_old_multipath() {
|
||||
ip -net "$nsrouter" -6 route add dead:dead::/64 \
|
||||
nexthop via dead:2::2 dev dummy0 \
|
||||
nexthop via dead:1::99 dev veth0
|
||||
}
|
||||
run_scenario "old-style multipath (sibling on veth0)" scenario_old_multipath
|
||||
|
||||
exit $ret
|
||||
|
|
@ -24,6 +24,8 @@ ALL_TESTS="
|
|||
kci_test_macsec
|
||||
kci_test_macsec_vlan
|
||||
kci_test_team_bridge_macvlan
|
||||
kci_test_bridge_promisc_netlink
|
||||
kci_test_bridge_promisc_sysfs
|
||||
kci_test_ipsec
|
||||
kci_test_ipsec_offload
|
||||
kci_test_fdb_get
|
||||
|
|
@ -61,6 +63,14 @@ check_fail()
|
|||
fi
|
||||
}
|
||||
|
||||
sysfs_write()
|
||||
{
|
||||
local val="$1"
|
||||
local path="$2"
|
||||
|
||||
echo "$val" > "$path"
|
||||
}
|
||||
|
||||
run_cmd_common()
|
||||
{
|
||||
local cmd="$*"
|
||||
|
|
@ -680,6 +690,59 @@ kci_test_team_bridge_macvlan()
|
|||
end_test "PASS: team_bridge_macvlan"
|
||||
}
|
||||
|
||||
# Test that changing bridge port flags via the netlink path does not sleep with
|
||||
# the bridge spin lock held.
|
||||
kci_test_bridge_promisc_netlink()
|
||||
{
|
||||
local dummy="test_dummy1"
|
||||
local bridge="test_br1"
|
||||
local team="test_team1"
|
||||
local ret=0
|
||||
|
||||
run_cmd ip link add $team up type team
|
||||
run_cmd ip link add $bridge up type bridge vlan_filtering 1
|
||||
run_cmd ip link add $dummy up type dummy
|
||||
run_cmd ip link set $dummy master $bridge
|
||||
run_cmd ip link set $team master $bridge
|
||||
|
||||
# This causes the bridge driver to sync all the static FDB entries to
|
||||
# the team device (which supports unicast filtering) and remove it from
|
||||
# promiscuous mode. The call to dev_set_promiscuity() can sleep due to
|
||||
# Rx mode inlining, which is a problem if the bridge spin lock is held.
|
||||
run_cmd bridge link set dev $dummy flood off learning off
|
||||
|
||||
run_cmd ip link del $dummy
|
||||
run_cmd ip link del $bridge
|
||||
run_cmd ip link del $team
|
||||
|
||||
end_test "PASS: bridge_promisc_netlink"
|
||||
}
|
||||
|
||||
# Same as kci_test_bridge_promisc_netlink(), but the flags are changed via the
|
||||
# sysfs path.
|
||||
kci_test_bridge_promisc_sysfs()
|
||||
{
|
||||
local dummy="test_dummy1"
|
||||
local bridge="test_br1"
|
||||
local team="test_team1"
|
||||
local ret=0
|
||||
|
||||
run_cmd ip link add $team up type team
|
||||
run_cmd ip link add $bridge up type bridge vlan_filtering 1
|
||||
run_cmd ip link add $dummy up type dummy
|
||||
run_cmd ip link set $dummy master $bridge
|
||||
run_cmd ip link set $team master $bridge
|
||||
|
||||
run_cmd sysfs_write 0 /sys/class/net/$dummy/brport/unicast_flood
|
||||
run_cmd sysfs_write 0 /sys/class/net/$dummy/brport/learning
|
||||
|
||||
run_cmd ip link del $dummy
|
||||
run_cmd ip link del $bridge
|
||||
run_cmd ip link del $team
|
||||
|
||||
end_test "PASS: bridge_promisc_sysfs"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Example commands
|
||||
# ip x s add proto esp src 14.0.0.52 dst 14.0.0.70 \
|
||||
|
|
|
|||
|
|
@ -1144,6 +1144,620 @@
|
|||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "531c",
|
||||
"name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$IP link set dev $DUMMY up || true",
|
||||
"$IP addr add 10.10.10.10/24 dev $DUMMY || true",
|
||||
"$TC qdisc add dev $DUMMY clsact",
|
||||
"$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 2"
|
||||
],
|
||||
"cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
|
||||
"expExitCode": "1",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 3
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY clsact",
|
||||
"$TC qdisc del dev $DEV1 clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "b1d7",
|
||||
"name": "Redirect singleport: dev1 ingress -> dev1 egress -> dev1 ingress (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DEV1 index 1"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DEV1 egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "egress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 3
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "c66d",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 egress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "aa99",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 ingress (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 2,
|
||||
"overlimits": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "37d7",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 ingress (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "egress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 3
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6d02",
|
||||
"name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress, different prios (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$IP link set dev $DUMMY up || true",
|
||||
"$IP addr add 10.10.10.10/24 dev $DUMMY || true",
|
||||
"$TC qdisc add dev $DUMMY clsact",
|
||||
"$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2"
|
||||
],
|
||||
"cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
|
||||
"expExitCode": "1",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 3
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY clsact",
|
||||
"$TC qdisc del dev $DEV1 clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8115",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress -> dev1 egress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact",
|
||||
"$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 12 matchall action mirred egress redirect dev $DEV1 index 3",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "9eb3",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 egress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "egress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d837",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy egress -> dummy ingress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "egress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2071",
|
||||
"name": "Redirect singleport: dev1 ingress -> dev1 ingress (Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1,
|
||||
"overlimits": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "0101",
|
||||
"name": "Redirect singleport: dummy egress -> dummy ingress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$IP addr add 10.10.10.10/24 dev $DUMMY || true",
|
||||
"$TC qdisc add dev $DUMMY clsact",
|
||||
"$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 1"
|
||||
],
|
||||
"cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
|
||||
"expExitCode": "1",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cf97",
|
||||
"name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress (No Loop)",
|
||||
"category": [
|
||||
"filter",
|
||||
"mirred"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": [
|
||||
"nsPlugin",
|
||||
"scapyPlugin"
|
||||
]
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DEV1 clsact",
|
||||
"$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
|
||||
"$TC qdisc add dev $DUMMY clsact"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2",
|
||||
"scapy": [
|
||||
{
|
||||
"iface": "$DEV0",
|
||||
"count": 1,
|
||||
"packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
|
||||
}
|
||||
],
|
||||
"expExitCode": "0",
|
||||
"verifyCmd": "$TC -j -s actions get action mirred index 1",
|
||||
"matchJSON": [
|
||||
{
|
||||
"total acts": 0
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"order": 1,
|
||||
"kind": "mirred",
|
||||
"mirred_action": "redirect",
|
||||
"direction": "ingress",
|
||||
"index": 1,
|
||||
"stats": {
|
||||
"packets": 1
|
||||
},
|
||||
"not_in_hw": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 clsact",
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -702,6 +702,7 @@
|
|||
"$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem duplicate 100%",
|
||||
"$TC filter add dev $DUMMY parent 1:0 protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1",
|
||||
"$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc ls m2 10Mbit",
|
||||
"$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%",
|
||||
"$TC filter add dev $DUMMY parent 1:0 protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2",
|
||||
"ping -c 1 10.10.10.1 -I$DUMMY > /dev/null || true",
|
||||
"$TC filter del dev $DUMMY parent 1:0 protocol ip prio 1",
|
||||
|
|
@ -714,8 +715,8 @@
|
|||
{
|
||||
"kind": "hfsc",
|
||||
"handle": "1:",
|
||||
"bytes": 294,
|
||||
"packets": 3
|
||||
"bytes": 392,
|
||||
"packets": 4
|
||||
}
|
||||
],
|
||||
"matchCount": "1",
|
||||
|
|
|
|||
|
|
@ -338,84 +338,34 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "d34d",
|
||||
"name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change root",
|
||||
"category": ["qdisc", "netem"],
|
||||
"id": "8c17",
|
||||
"name": "Test netem's recursive duplicate",
|
||||
"category": [
|
||||
"qdisc",
|
||||
"netem"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": "nsPlugin"
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DUMMY root handle 1: netem limit 1",
|
||||
"$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1"
|
||||
"$IP link set dev $DUMMY up || true",
|
||||
"$IP addr add 10.10.11.10/24 dev $DUMMY || true",
|
||||
"$TC qdisc add dev $DUMMY root handle 1: netem limit 1000 duplicate 100%",
|
||||
"$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1000 duplicate 100%"
|
||||
],
|
||||
"cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: netem duplicate 50%",
|
||||
"expExitCode": "2",
|
||||
"verifyCmd": "$TC -s qdisc show dev $DUMMY",
|
||||
"matchPattern": "qdisc netem",
|
||||
"matchCount": "2",
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY handle 1:0 root"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "b33f",
|
||||
"name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change non-root",
|
||||
"category": ["qdisc", "netem"],
|
||||
"plugins": {
|
||||
"requires": "nsPlugin"
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DUMMY root handle 1: netem limit 1",
|
||||
"$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1"
|
||||
"cmdUnderTest": "ping -c 1 10.10.11.11 -W 0.01",
|
||||
"expExitCode": "1",
|
||||
"verifyCmd": "$TC -s -j qdisc ls dev $DUMMY root",
|
||||
"matchJSON": [
|
||||
{
|
||||
"kind": "netem",
|
||||
"handle": "1:",
|
||||
"bytes": 294,
|
||||
"packets": 3
|
||||
}
|
||||
],
|
||||
"cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2: netem duplicate 50%",
|
||||
"expExitCode": "2",
|
||||
"verifyCmd": "$TC -s qdisc show dev $DUMMY",
|
||||
"matchPattern": "qdisc netem",
|
||||
"matchCount": "2",
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY handle 1:0 root"
|
||||
"$TC qdisc del dev $DUMMY handle 1: root"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cafe",
|
||||
"name": "NETEM test qdisc duplication restriction in qdisc tree",
|
||||
"category": ["qdisc", "netem"],
|
||||
"plugins": {
|
||||
"requires": "nsPlugin"
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%"
|
||||
],
|
||||
"cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%",
|
||||
"expExitCode": "2",
|
||||
"verifyCmd": "$TC -s qdisc show dev $DUMMY",
|
||||
"matchPattern": "qdisc netem",
|
||||
"matchCount": "1",
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY handle 1:0 root"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "1337",
|
||||
"name": "NETEM test qdisc duplication restriction in qdisc tree across branches",
|
||||
"category": ["qdisc", "netem"],
|
||||
"plugins": {
|
||||
"requires": "nsPlugin"
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DUMMY parent root handle 1:0 hfsc",
|
||||
"$TC class add dev $DUMMY parent 1:0 classid 1:1 hfsc rt m2 10Mbit",
|
||||
"$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem",
|
||||
"$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc rt m2 10Mbit"
|
||||
],
|
||||
"cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%",
|
||||
"expExitCode": "2",
|
||||
"verifyCmd": "$TC -s qdisc show dev $DUMMY",
|
||||
"matchPattern": "qdisc netem",
|
||||
"matchCount": "1",
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY handle 1:0 root"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user