mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-19 07:38:40 +02:00
Merge branch 'xfrm: XFRM_MSG_MIGRATE_STATE new netlink message'
Antony Antony says: ==================== The current XFRM_MSG_MIGRATE interface is tightly coupled to policy and SA migration, and it lacks the information required to reliably migrate individual SAs. This makes it unsuitable for IKEv2 deployments, dual-stack setups (IPv4/IPv6), and scenarios where policies are managed externally (e.g., by daemons other than the IKE daemon). Mandatory SA selector list The current API requires a non-empty SA selector list, which does not reflect the IKEv2 use case. A single Child SA may correspond to multiple policies, and SA discovery already occurs via address and reqid matching. With dual-stack Child SAs this leads to excessive churn: the current method would have to be called up to six times (in/out/fwd × v4/v6) on SA, while the new method only requires two calls. Selectors lack SPI (and marks) XFRM_MSG_MIGRATE cannot uniquely identify an SA when multiple SAs share the same policies (per-CPU SAs, SELinux label-based SAs, etc.). Without the SPI, the kernel may update the wrong SA instance. Reqid cannot be changed Some implementations allocate reqids based on traffic selectors. In host-to-host or selector-changing scenarios, the reqid must change, which the current API cannot express. Because strongSwan and other implementations manage policies independently of the kernel, an interface that updates only a specific SA - with complete and unambiguous identification - is required. SA Selector, x->sel, can't be changed, especially Transport mode. XFRM_MSG_MIGRATE_STATE provides that interface. It supports migration of a single SA via xfrm_usersa_id (including SPI) and we fix encap removal in this patch set, reqid updates, address changes, and other SA-specific parameters. It avoids the structural limitations of XFRM_MSG_MIGRATE and provides a simpler, extensible mechanism for precise per-SA migration without involving policies. This method also allows migtrating SA selectors typically used with host-to-host in Transport mode. New migration steps: first install block policy, remove the old policy, call XFRM_MSG_MIGRATE_STATE for each state, then re-install the policies and remove the block policy. If the target SA tuple (daddr, SPI, proto, family) is already occupied, the operation returns -EEXIST. In this case the original SA is not preserved. Userspace must handle -EEXIST by re-establishing the SA at the IKE level and manage policies. ==================== Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
@@ -9,5 +9,6 @@ XFRM Framework
|
||||
|
||||
xfrm_device
|
||||
xfrm_proc
|
||||
xfrm_migrate_state
|
||||
xfrm_sync
|
||||
xfrm_sysctl
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=====================
|
||||
XFRM SA Migrate State
|
||||
=====================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
``XFRM_MSG_MIGRATE_STATE`` migrates a single SA, looked up using SPI and
|
||||
mark, without involving policies. Unlike ``XFRM_MSG_MIGRATE``, which couples
|
||||
SA and policy migration and allows migrating multiple SAs in one call, this
|
||||
interface identifies the SA unambiguously via SPI and supports changing
|
||||
the reqid, addresses, encapsulation, selector, and offload.
|
||||
|
||||
Because IKE daemons can manage policies independently of
|
||||
the kernel, this interface allows precise per-SA migration without
|
||||
requiring policy involvement. Optional netlink attributes follow an
|
||||
omit-to-inherit model: omitting an attribute preserves the value from
|
||||
the old SA. The ``flags`` field controls two exceptions: hardware offload
|
||||
is inherited by default and can be suppressed with
|
||||
``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` or overridden with ``XFRMA_OFFLOAD_DEV``;
|
||||
the new selector is taken from ``new_sel`` by default and can instead be
|
||||
derived from the new addresses with ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL``.
|
||||
|
||||
SA Identification
|
||||
=================
|
||||
|
||||
The struct is defined in ``include/uapi/linux/xfrm.h``. The SA is looked
|
||||
up using ``xfrm_state_lookup()`` with ``id.spi``,
|
||||
``id.daddr``, ``id.proto``, ``id.family``, and
|
||||
``old_mark.v & old_mark.m`` as the mark key::
|
||||
|
||||
struct xfrm_user_migrate_state {
|
||||
struct xfrm_usersa_id id; /* spi, daddr, proto, family */
|
||||
xfrm_address_t new_daddr;
|
||||
xfrm_address_t new_saddr;
|
||||
struct xfrm_mark old_mark; /* SA lookup: key = v & m */
|
||||
struct xfrm_selector new_sel; /* new selector (see Flags) */
|
||||
__u32 new_reqid;
|
||||
__u32 flags; /* XFRM_MIGRATE_STATE_* */
|
||||
__u16 new_family;
|
||||
__u16 reserved; /* must be zero */
|
||||
};
|
||||
|
||||
The ``reserved`` field must be set to zero; the kernel rejects any
|
||||
other value with ``-EINVAL``.
|
||||
|
||||
Supported Attributes
|
||||
====================
|
||||
|
||||
The following fields in ``xfrm_user_migrate_state`` are always explicit
|
||||
and are not inherited from the existing SA. Passing zero is not equivalent
|
||||
to "keep unchanged" — zero is used as-is:
|
||||
|
||||
- ``new_daddr`` - new destination address
|
||||
- ``new_saddr`` - new source address
|
||||
- ``new_family`` - new address family
|
||||
- ``new_reqid`` - new reqid (0 = no reqid)
|
||||
- ``new_sel`` - new selector; used when ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` is
|
||||
not set (see `Flags`_ below)
|
||||
- ``flags`` - bitmask of ``XFRM_MIGRATE_STATE_*`` flags (see `Flags`_ below)
|
||||
|
||||
The following netlink attributes are also accepted. Omitting an attribute
|
||||
inherits the value from the existing SA (omit-to-inherit).
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Attribute
|
||||
- Description
|
||||
* - ``XFRMA_MARK``
|
||||
- Mark on the migrated SA (``struct xfrm_mark``). Absent inherits
|
||||
``old_mark``. To use no mark on the new SA, send ``XFRMA_MARK``
|
||||
with ``{0, 0}``.
|
||||
* - ``XFRMA_ENCAP``
|
||||
- UDP encapsulation template; only ``UDP_ENCAP_ESPINUDP`` is supported.
|
||||
Set ``encap_type=0`` to remove encap.
|
||||
* - ``XFRMA_OFFLOAD_DEV``
|
||||
- Hardware offload configuration (``struct xfrm_user_offload``). Absent
|
||||
copies offload from the existing SA. When
|
||||
``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` is set in ``flags``, the new SA has
|
||||
no offload; this flag is mutually exclusive with ``XFRMA_OFFLOAD_DEV``
|
||||
and sending both returns ``-EINVAL``.
|
||||
* - ``XFRMA_SET_MARK``
|
||||
- Output mark on the migrated SA; pair with ``XFRMA_SET_MARK_MASK``.
|
||||
Send 0 to clear.
|
||||
* - ``XFRMA_NAT_KEEPALIVE_INTERVAL``
|
||||
- NAT keepalive interval in seconds. Requires encap. Send 0 to clear.
|
||||
Automatically cleared when encap is removed; setting a non-zero
|
||||
value without encap returns ``-EINVAL``.
|
||||
* - ``XFRMA_MTIMER_THRESH``
|
||||
- Mapping maxage threshold. Only valid on input SAs; setting on an
|
||||
output SA returns ``-EINVAL``. Requires encap. Send 0 to clear.
|
||||
Automatically cleared when encap is removed; setting a non-zero
|
||||
value without encap returns ``-EINVAL``.
|
||||
|
||||
The following SA properties are immutable and cannot be changed via
|
||||
``XFRM_MSG_MIGRATE_STATE``: algorithms (``XFRMA_ALG_*``), replay state,
|
||||
direction (``XFRMA_SA_DIR``), and security context (``XFRMA_SEC_CTX``).
|
||||
|
||||
Flags
|
||||
=====
|
||||
|
||||
The ``flags`` field in ``xfrm_user_migrate_state`` controls optional
|
||||
migration behaviour. Unknown flag bits are rejected with ``-EINVAL``; the
|
||||
extended ACK message identifies the unrecognised bits (e.g. ``"Unknown flags:
|
||||
0x4"``). Userspace can use ``XFRM_MIGRATE_STATE_KNOWN_FLAGS`` (defined in
|
||||
``<linux/xfrm.h>``) to validate flags before sending; note that this constant
|
||||
reflects the flags known to the header version userspace was compiled against,
|
||||
which may differ from what the running kernel accepts.
|
||||
|
||||
.. list-table::
|
||||
:widths: 40 60
|
||||
:header-rows: 1
|
||||
|
||||
* - Flag
|
||||
- Description
|
||||
* - ``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD``
|
||||
- When set, the new SA has no hardware offload even when
|
||||
``XFRMA_OFFLOAD_DEV`` is absent. Without this flag, omitting
|
||||
``XFRMA_OFFLOAD_DEV`` copies the existing offload to the new SA.
|
||||
Mutually exclusive with ``XFRMA_OFFLOAD_DEV``; sending both
|
||||
returns ``-EINVAL``.
|
||||
* - ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL``
|
||||
- When set, the kernel validates that the existing SA selector is a
|
||||
single-host entry matching the SA addresses (``prefixlen_s ==
|
||||
prefixlen_d`` equal to 32 for IPv4 or 128 for IPv6, and addresses
|
||||
matching ``id.daddr`` and ``props.saddr``). If the check passes,
|
||||
the new selector is derived from ``new_daddr`` and ``new_saddr``
|
||||
with the single-host mask for ``new_family``. A mismatch returns
|
||||
``-EINVAL``. When this flag is not set, ``new_sel`` is used as-is
|
||||
for the migrated SA.
|
||||
|
||||
Migration Steps
|
||||
===============
|
||||
|
||||
Outgoing SA
|
||||
-----------
|
||||
|
||||
To prevent cleartext traffic leaks, install a block policy before
|
||||
migrating:
|
||||
|
||||
#. Install a block policy to drop traffic on the affected selector.
|
||||
#. Remove the old policy.
|
||||
#. Call ``XFRM_MSG_MIGRATE_STATE`` for each SA.
|
||||
#. Reinstall the policies.
|
||||
#. Remove the block policy.
|
||||
|
||||
If AES-GCM is in use, the block policy also prevents IV reuse during
|
||||
the migration window. For other AEADs this step is not required for
|
||||
IV safety, but skipping it allows a brief cleartext window.
|
||||
|
||||
Incoming SA
|
||||
-----------
|
||||
|
||||
No block policy is needed. ``XFRM_MSG_MIGRATE_STATE`` atomically
|
||||
transfers the sequence number and replay window from the old SA to
|
||||
the new SA, so the new SA continues replay protection without a gap.
|
||||
Call ``XFRM_MSG_MIGRATE_STATE`` for each SA directly.
|
||||
|
||||
When accepting incoming traffic, be liberal during the migration
|
||||
window: packets sent by the remote peer before it completed its own
|
||||
migration may arrive out of order or slightly late. Dropping them
|
||||
unnecessarily causes packet loss. A generous replay window reduces
|
||||
the impact of reordering during migration.
|
||||
|
||||
Block Policy and IV Safety
|
||||
--------------------------
|
||||
|
||||
AES-GCM IV uniqueness is critical: reusing a (key, IV) pair allows
|
||||
an attacker to recover the authentication subkey and forge
|
||||
authentication tags, breaking both confidentiality and integrity.
|
||||
This concern applies to outgoing SAs only — the remote peer controls
|
||||
IV generation on incoming traffic.
|
||||
|
||||
``XFRM_MSG_MIGRATE_STATE`` atomically copies the sequence number and
|
||||
replay window from the old SA to the new SA and deletes the old SA.
|
||||
The block policy serves two purposes: it prevents cleartext traffic
|
||||
leaks during the migration window, and for AES-GCM it prevents IV
|
||||
reuse by ensuring no outgoing packets are sent under the same key.
|
||||
The atomic copy of the sequence number and replay window complements
|
||||
this — together they eliminate both risks during migration.
|
||||
The atomic copy also serves incoming SAs, ensuring replay protection
|
||||
continues without a gap across the migration.
|
||||
|
||||
Feature Detection
|
||||
=================
|
||||
|
||||
Userspace can probe for kernel support by sending a minimal
|
||||
``XFRM_MSG_MIGRATE_STATE`` message with a non-zero non-existent SPI:
|
||||
|
||||
- ``-EINVAL``: kernel predates ``XFRM_MSG_MIGRATE_STATE``; message type
|
||||
is out of range
|
||||
- ``-ENOPROTOOPT``: message type is known but ``CONFIG_XFRM_MIGRATE``
|
||||
is not enabled
|
||||
- ``-ESRCH``: supported (SPI not found)
|
||||
|
||||
Userspace Notification on Success
|
||||
=================================
|
||||
|
||||
On successful migration the kernel multicasts an
|
||||
``XFRM_MSG_MIGRATE_STATE`` message to the ``XFRMNLGRP_MIGRATE`` group.
|
||||
The fixed header is ``struct xfrm_user_migrate_state`` copied from the
|
||||
request, followed by the same set of netlink attributes that are
|
||||
accepted as input, with the differences noted below.
|
||||
|
||||
Differences from the request
|
||||
-----------------------------
|
||||
|
||||
.. list-table::
|
||||
:widths: 25 75
|
||||
:header-rows: 1
|
||||
|
||||
* - Field / Attribute
|
||||
- Difference
|
||||
* - ``new_sel``
|
||||
- Contains the actual selector of the newly installed SA, not the
|
||||
``new_sel`` from the request. When
|
||||
``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` is set the kernel derives the
|
||||
selector from ``new_daddr`` / ``new_saddr``; the caller's
|
||||
``new_sel`` field is ignored in that case. The notification
|
||||
always carries the real selector of the new SA.
|
||||
* - ``XFRMA_SA_DIR``
|
||||
- Present in the notification (set from the direction of the new
|
||||
SA) but **not accepted as input** — direction is immutable.
|
||||
* - ``flags``
|
||||
- Echoed back as-is. ``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` and
|
||||
``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` describe the request that was
|
||||
made, not a property of the resulting SA.
|
||||
|
||||
Attributes in the notification
|
||||
-------------------------------
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Attribute
|
||||
- Description
|
||||
* - ``XFRMA_ENCAP``
|
||||
- UDP encapsulation template, if configured on the new SA.
|
||||
* - ``XFRMA_OFFLOAD_DEV``
|
||||
- Hardware offload configuration, if active on the new SA.
|
||||
* - ``XFRMA_MARK``
|
||||
- Mark on the new SA, if set.
|
||||
* - ``XFRMA_SET_MARK``
|
||||
- Output mark on the new SA, if set.
|
||||
* - ``XFRMA_SET_MARK_MASK``
|
||||
- Output mark mask, present together with ``XFRMA_SET_MARK``.
|
||||
* - ``XFRMA_MTIMER_THRESH``
|
||||
- Mapping maxage threshold, if non-zero.
|
||||
* - ``XFRMA_NAT_KEEPALIVE_INTERVAL``
|
||||
- NAT keepalive interval, if non-zero.
|
||||
* - ``XFRMA_SA_DIR``
|
||||
- Direction of the new SA.
|
||||
|
||||
Error Handling
|
||||
==============
|
||||
|
||||
If the target SA tuple (new daddr, SPI, proto, new family) is already
|
||||
occupied, the operation returns ``-EEXIST`` before the migration begins.
|
||||
The old SA remains intact and the operation is safe to retry after
|
||||
resolving the conflict.
|
||||
|
||||
If the target SA is deleted before the migration completes, the operation
|
||||
returns ``-ESRCH``. No new SA is installed. Userspace should verify the
|
||||
current SA state before retrying.
|
||||
|
||||
If the multicast notification (``XFRMNLGRP_MIGRATE``) fails to send,
|
||||
the migration itself has already completed successfully and the new SA
|
||||
is installed. The operation returns success, 0, with an extack warning,
|
||||
but listeners will not receive the migration event.
|
||||
+62
-16
@@ -682,12 +682,22 @@ struct xfrm_migrate {
|
||||
xfrm_address_t old_saddr;
|
||||
xfrm_address_t new_daddr;
|
||||
xfrm_address_t new_saddr;
|
||||
struct xfrm_encap_tmpl *encap;
|
||||
struct xfrm_user_offload *xuo;
|
||||
struct xfrm_mark old_mark;
|
||||
const struct xfrm_mark *new_mark;
|
||||
struct xfrm_mark smark;
|
||||
u8 proto;
|
||||
u8 mode;
|
||||
u16 reserved;
|
||||
u32 reqid;
|
||||
u16 msg_type; /* XFRM_MSG_MIGRATE or XFRM_MSG_MIGRATE_STATE */
|
||||
u32 flags;
|
||||
u32 old_reqid;
|
||||
u32 new_reqid;
|
||||
u32 nat_keepalive_interval;
|
||||
u32 mapping_maxage;
|
||||
u16 old_family;
|
||||
u16 new_family;
|
||||
const struct xfrm_selector *new_sel;
|
||||
};
|
||||
|
||||
#define XFRM_KM_TIMEOUT 30
|
||||
@@ -1774,7 +1784,7 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
|
||||
int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack);
|
||||
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
|
||||
int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack);
|
||||
int xfrm_init_state(struct xfrm_state *x);
|
||||
int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack);
|
||||
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
|
||||
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
|
||||
int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
|
||||
@@ -1895,11 +1905,17 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
const struct xfrm_encap_tmpl *encap);
|
||||
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
|
||||
u32 if_id);
|
||||
struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
|
||||
const struct xfrm_migrate *m,
|
||||
struct net *net,
|
||||
struct netlink_ext_ack *extack);
|
||||
int xfrm_state_migrate_install(const struct xfrm_state *x,
|
||||
struct xfrm_state *xc,
|
||||
const struct xfrm_migrate *m,
|
||||
struct netlink_ext_ack *extack);
|
||||
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
|
||||
struct xfrm_migrate *m,
|
||||
struct xfrm_encap_tmpl *encap,
|
||||
struct net *net,
|
||||
struct xfrm_user_offload *xuo,
|
||||
struct netlink_ext_ack *extack);
|
||||
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
struct xfrm_migrate *m, int num_bundles,
|
||||
@@ -2013,23 +2029,51 @@ static inline unsigned int xfrm_replay_state_esn_len(struct xfrm_replay_state_es
|
||||
|
||||
#ifdef CONFIG_XFRM_MIGRATE
|
||||
static inline int xfrm_replay_clone(struct xfrm_state *x,
|
||||
struct xfrm_state *orig)
|
||||
const struct xfrm_state *orig)
|
||||
{
|
||||
/* Counters synced later in xfrm_replay_sync() */
|
||||
|
||||
x->replay_esn = kmemdup(orig->replay_esn,
|
||||
x->replay = orig->replay;
|
||||
x->preplay = orig->preplay;
|
||||
|
||||
if (orig->replay_esn) {
|
||||
x->replay_esn = kmemdup(orig->replay_esn,
|
||||
xfrm_replay_state_esn_len(orig->replay_esn),
|
||||
GFP_KERNEL);
|
||||
if (!x->replay_esn)
|
||||
return -ENOMEM;
|
||||
x->preplay_esn = kmemdup(orig->preplay_esn,
|
||||
xfrm_replay_state_esn_len(orig->preplay_esn),
|
||||
GFP_KERNEL);
|
||||
if (!x->preplay_esn)
|
||||
return -ENOMEM;
|
||||
if (!x->replay_esn)
|
||||
return -ENOMEM;
|
||||
x->preplay_esn = kmemdup(orig->preplay_esn,
|
||||
xfrm_replay_state_esn_len(orig->preplay_esn),
|
||||
GFP_KERNEL);
|
||||
if (!x->preplay_esn)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void xfrm_replay_sync(struct xfrm_state *x, const struct xfrm_state *orig)
|
||||
{
|
||||
x->replay = orig->replay;
|
||||
x->preplay = orig->preplay;
|
||||
|
||||
if (orig->replay_esn) {
|
||||
memcpy(x->replay_esn, orig->replay_esn,
|
||||
xfrm_replay_state_esn_len(orig->replay_esn));
|
||||
|
||||
memcpy(x->preplay_esn, orig->preplay_esn,
|
||||
xfrm_replay_state_esn_len(orig->preplay_esn));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void xfrm_migrate_sync(struct xfrm_state *x,
|
||||
const struct xfrm_state *orig)
|
||||
{
|
||||
/* called under lock so no race conditions or mallocs allowed */
|
||||
memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft));
|
||||
xfrm_replay_sync(x, orig);
|
||||
}
|
||||
|
||||
static inline struct xfrm_algo_aead *xfrm_algo_aead_clone(struct xfrm_algo_aead *orig)
|
||||
{
|
||||
return kmemdup(orig, aead_len(orig), GFP_KERNEL);
|
||||
@@ -2068,7 +2112,7 @@ void xfrm_dev_resume(struct sk_buff *skb);
|
||||
void xfrm_dev_backlog(struct softnet_data *sd);
|
||||
struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
|
||||
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||
struct xfrm_user_offload *xuo,
|
||||
const struct xfrm_user_offload *xuo,
|
||||
struct netlink_ext_ack *extack);
|
||||
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
|
||||
struct xfrm_user_offload *xuo, u8 dir,
|
||||
@@ -2139,7 +2183,9 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack)
|
||||
static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||
const struct xfrm_user_offload *xuo,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -227,6 +227,9 @@ enum {
|
||||
#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT
|
||||
XFRM_MSG_GETDEFAULT,
|
||||
#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT
|
||||
|
||||
XFRM_MSG_MIGRATE_STATE,
|
||||
#define XFRM_MSG_MIGRATE_STATE XFRM_MSG_MIGRATE_STATE
|
||||
__XFRM_MSG_MAX
|
||||
};
|
||||
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
|
||||
@@ -507,6 +510,28 @@ struct xfrm_user_migrate {
|
||||
__u16 new_family;
|
||||
};
|
||||
|
||||
struct xfrm_user_migrate_state {
|
||||
struct xfrm_usersa_id id;
|
||||
xfrm_address_t new_daddr;
|
||||
xfrm_address_t new_saddr;
|
||||
struct xfrm_mark old_mark;
|
||||
struct xfrm_selector new_sel;
|
||||
__u32 new_reqid;
|
||||
__u32 flags;
|
||||
__u16 new_family;
|
||||
__u16 reserved;
|
||||
};
|
||||
|
||||
/* Flags for xfrm_user_migrate_state.flags */
|
||||
enum xfrm_migrate_state_flags {
|
||||
XFRM_MIGRATE_STATE_CLEAR_OFFLOAD = 1, /* do not inherit offload from existing SA */
|
||||
XFRM_MIGRATE_STATE_UPDATE_H2H_SEL = 2, /* update H2H selector from new daddr/saddr */
|
||||
};
|
||||
|
||||
/* All flags defined as of this header version; unknown bits are rejected. */
|
||||
#define XFRM_MIGRATE_STATE_KNOWN_FLAGS \
|
||||
(XFRM_MIGRATE_STATE_CLEAR_OFFLOAD | XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)
|
||||
|
||||
struct xfrm_user_mapping {
|
||||
struct xfrm_usersa_id id;
|
||||
__u32 reqid;
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
|
||||
memcpy(&t->mark, &x->mark, sizeof(t->mark));
|
||||
t->if_id = x->if_id;
|
||||
|
||||
if (xfrm_init_state(t))
|
||||
if (xfrm_init_state(t, NULL))
|
||||
goto error;
|
||||
|
||||
atomic_set(&t->tunnel_users, 1);
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
|
||||
memcpy(&t->mark, &x->mark, sizeof(t->mark));
|
||||
t->if_id = x->if_id;
|
||||
|
||||
if (xfrm_init_state(t))
|
||||
if (xfrm_init_state(t, NULL))
|
||||
goto error;
|
||||
|
||||
atomic_set(&t->tunnel_users, 1);
|
||||
|
||||
+6
-6
@@ -1299,7 +1299,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
|
||||
}
|
||||
}
|
||||
|
||||
err = xfrm_init_state(x);
|
||||
err = xfrm_init_state(x, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -2554,7 +2554,7 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len,
|
||||
if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0)
|
||||
return -EINVAL;
|
||||
m->mode = mode;
|
||||
m->reqid = rq1->sadb_x_ipsecrequest_reqid;
|
||||
m->old_reqid = rq1->sadb_x_ipsecrequest_reqid;
|
||||
|
||||
return ((int)(rq1->sadb_x_ipsecrequest_len +
|
||||
rq2->sadb_x_ipsecrequest_len));
|
||||
@@ -3655,15 +3655,15 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
if (mode < 0)
|
||||
goto err;
|
||||
if (set_ipsecrequest(skb, mp->proto, mode,
|
||||
(mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
|
||||
mp->reqid, mp->old_family,
|
||||
(mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
|
||||
mp->old_reqid, mp->old_family,
|
||||
&mp->old_saddr, &mp->old_daddr) < 0)
|
||||
goto err;
|
||||
|
||||
/* new ipsecrequest */
|
||||
if (set_ipsecrequest(skb, mp->proto, mode,
|
||||
(mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
|
||||
mp->reqid, mp->new_family,
|
||||
(mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE),
|
||||
mp->old_reqid, mp->new_family,
|
||||
&mp->new_saddr, &mp->new_daddr) < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
|
||||
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
|
||||
[XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
|
||||
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
|
||||
[XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping)
|
||||
[XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping),
|
||||
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
|
||||
};
|
||||
|
||||
static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
|
||||
@@ -162,6 +163,7 @@ static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
|
||||
case XFRM_MSG_NEWAE:
|
||||
case XFRM_MSG_REPORT:
|
||||
case XFRM_MSG_MIGRATE:
|
||||
case XFRM_MSG_MIGRATE_STATE:
|
||||
case XFRM_MSG_NEWSADINFO:
|
||||
case XFRM_MSG_NEWSPDINFO:
|
||||
case XFRM_MSG_MAPPING:
|
||||
@@ -498,6 +500,7 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
|
||||
case XFRM_MSG_GETAE:
|
||||
case XFRM_MSG_REPORT:
|
||||
case XFRM_MSG_MIGRATE:
|
||||
case XFRM_MSG_MIGRATE_STATE:
|
||||
case XFRM_MSG_NEWSADINFO:
|
||||
case XFRM_MSG_GETSADINFO:
|
||||
case XFRM_MSG_NEWSPDINFO:
|
||||
|
||||
@@ -229,7 +229,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
|
||||
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
|
||||
|
||||
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||
struct xfrm_user_offload *xuo,
|
||||
const struct xfrm_user_offload *xuo,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
|
||||
+22
-3
@@ -4538,7 +4538,7 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm
|
||||
int match = 0;
|
||||
|
||||
if (t->mode == m->mode && t->id.proto == m->proto &&
|
||||
(m->reqid == 0 || t->reqid == m->reqid)) {
|
||||
(m->old_reqid == 0 || t->reqid == m->old_reqid)) {
|
||||
switch (t->mode) {
|
||||
case XFRM_MODE_TUNNEL:
|
||||
case XFRM_MODE_BEET:
|
||||
@@ -4632,7 +4632,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
|
||||
sizeof(m[i].old_saddr)) &&
|
||||
m[i].proto == m[j].proto &&
|
||||
m[i].mode == m[j].mode &&
|
||||
m[i].reqid == m[j].reqid &&
|
||||
m[i].old_reqid == m[j].old_reqid &&
|
||||
m[i].old_family == m[j].old_family) {
|
||||
NL_SET_ERR_MSG(extack, "Entries in the MIGRATE attribute's list must be unique");
|
||||
return -EINVAL;
|
||||
@@ -4643,6 +4643,21 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill migrate fields that are invariant in XFRM_MSG_MIGRATE: inherited
|
||||
* from the existing SA unchanged. XFRM_MSG_MIGRATE_STATE can update these.
|
||||
*/
|
||||
static void xfrm_migrate_copy_old(const struct xfrm_state *x,
|
||||
struct xfrm_migrate *mp)
|
||||
{
|
||||
mp->msg_type = XFRM_MSG_MIGRATE;
|
||||
mp->smark = x->props.smark;
|
||||
mp->new_reqid = x->props.reqid;
|
||||
mp->nat_keepalive_interval = x->nat_keepalive_interval;
|
||||
mp->mapping_maxage = x->mapping_maxage;
|
||||
mp->new_mark = &x->mark;
|
||||
}
|
||||
|
||||
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
struct xfrm_migrate *m, int num_migrate,
|
||||
struct xfrm_kmaddress *k, struct net *net,
|
||||
@@ -4680,7 +4695,11 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
if ((x = xfrm_migrate_state_find(mp, net, if_id))) {
|
||||
x_cur[nx_cur] = x;
|
||||
nx_cur++;
|
||||
xc = xfrm_state_migrate(x, mp, encap, net, xuo, extack);
|
||||
mp->encap = encap;
|
||||
mp->xuo = xuo;
|
||||
xfrm_migrate_copy_old(x, mp);
|
||||
|
||||
xc = xfrm_state_migrate(x, mp, net, extack);
|
||||
if (xc) {
|
||||
x_new[nx_new] = xc;
|
||||
nx_new++;
|
||||
|
||||
+95
-57
@@ -1966,8 +1966,7 @@ static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *secu
|
||||
}
|
||||
|
||||
static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
struct xfrm_encap_tmpl *encap,
|
||||
struct xfrm_migrate *m)
|
||||
const struct xfrm_migrate *m)
|
||||
{
|
||||
struct net *net = xs_net(orig);
|
||||
struct xfrm_state *x = xfrm_state_alloc(net);
|
||||
@@ -1975,13 +1974,25 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
goto out;
|
||||
|
||||
memcpy(&x->id, &orig->id, sizeof(x->id));
|
||||
memcpy(&x->sel, &orig->sel, sizeof(x->sel));
|
||||
if (m->msg_type == XFRM_MSG_MIGRATE_STATE) {
|
||||
if (m->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) {
|
||||
u8 prefixlen = (m->new_family == AF_INET6) ? 128 : 32;
|
||||
|
||||
x->sel = orig->sel;
|
||||
x->sel.family = m->new_family;
|
||||
x->sel.prefixlen_d = prefixlen;
|
||||
x->sel.prefixlen_s = prefixlen;
|
||||
x->sel.daddr = m->new_daddr;
|
||||
x->sel.saddr = m->new_saddr;
|
||||
} else {
|
||||
x->sel = *m->new_sel;
|
||||
}
|
||||
} else {
|
||||
x->sel = orig->sel;
|
||||
}
|
||||
memcpy(&x->lft, &orig->lft, sizeof(x->lft));
|
||||
x->props.mode = orig->props.mode;
|
||||
x->props.replay_window = orig->props.replay_window;
|
||||
x->props.reqid = orig->props.reqid;
|
||||
x->props.family = orig->props.family;
|
||||
x->props.saddr = orig->props.saddr;
|
||||
|
||||
if (orig->aalg) {
|
||||
x->aalg = xfrm_algo_auth_clone(orig->aalg);
|
||||
@@ -2010,16 +2021,12 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
}
|
||||
x->props.calgo = orig->props.calgo;
|
||||
|
||||
if (encap || orig->encap) {
|
||||
if (encap)
|
||||
x->encap = kmemdup(encap, sizeof(*x->encap),
|
||||
GFP_KERNEL);
|
||||
else
|
||||
x->encap = kmemdup(orig->encap, sizeof(*x->encap),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (m->encap) {
|
||||
x->encap = kmemdup(m->encap, sizeof(*x->encap), GFP_KERNEL);
|
||||
if (!x->encap)
|
||||
goto error;
|
||||
x->mapping_maxage = m->mapping_maxage;
|
||||
x->nat_keepalive_interval = m->nat_keepalive_interval;
|
||||
}
|
||||
|
||||
if (orig->security)
|
||||
@@ -2033,13 +2040,12 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (orig->replay_esn) {
|
||||
if (xfrm_replay_clone(x, orig))
|
||||
goto error;
|
||||
}
|
||||
if (xfrm_replay_clone(x, orig))
|
||||
goto error;
|
||||
|
||||
memcpy(&x->mark, &orig->mark, sizeof(x->mark));
|
||||
memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark));
|
||||
x->mark = m->new_mark ? *m->new_mark : m->old_mark;
|
||||
|
||||
x->props.smark = m->smark;
|
||||
|
||||
x->props.flags = orig->props.flags;
|
||||
x->props.extra_flags = orig->props.extra_flags;
|
||||
@@ -2049,12 +2055,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
x->tfcpad = orig->tfcpad;
|
||||
x->replay_maxdiff = orig->replay_maxdiff;
|
||||
x->replay_maxage = orig->replay_maxage;
|
||||
memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft));
|
||||
x->km.state = orig->km.state;
|
||||
x->km.seq = orig->km.seq;
|
||||
x->replay = orig->replay;
|
||||
x->preplay = orig->preplay;
|
||||
x->mapping_maxage = orig->mapping_maxage;
|
||||
x->lastused = orig->lastused;
|
||||
x->new_mapping = 0;
|
||||
x->new_mapping_sport = 0;
|
||||
@@ -2066,7 +2068,7 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
x->props.reqid = m->new_reqid;
|
||||
x->props.family = m->new_family;
|
||||
memcpy(&x->id.daddr, &m->new_daddr, sizeof(x->id.daddr));
|
||||
memcpy(&x->props.saddr, &m->new_saddr, sizeof(x->props.saddr));
|
||||
@@ -2088,14 +2090,14 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
if (m->reqid) {
|
||||
if (m->old_reqid) {
|
||||
h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr,
|
||||
m->reqid, m->old_family);
|
||||
m->old_reqid, m->old_family);
|
||||
hlist_for_each_entry(x, xfrm_state_deref_prot(net->xfrm.state_bydst, net) + h, bydst) {
|
||||
if (x->props.mode != m->mode ||
|
||||
x->id.proto != m->proto)
|
||||
continue;
|
||||
if (m->reqid && x->props.reqid != m->reqid)
|
||||
if (m->old_reqid && x->props.reqid != m->old_reqid)
|
||||
continue;
|
||||
if (if_id != 0 && x->if_id != if_id)
|
||||
continue;
|
||||
@@ -2132,45 +2134,81 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_migrate_state_find);
|
||||
|
||||
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
|
||||
struct xfrm_migrate *m,
|
||||
struct xfrm_encap_tmpl *encap,
|
||||
struct net *net,
|
||||
struct xfrm_user_offload *xuo,
|
||||
struct netlink_ext_ack *extack)
|
||||
struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x,
|
||||
const struct xfrm_migrate *m,
|
||||
struct net *net,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct xfrm_state *xc;
|
||||
|
||||
xc = xfrm_state_clone_and_setup(x, encap, m);
|
||||
if (!xc)
|
||||
xc = xfrm_state_clone_and_setup(x, m);
|
||||
if (!xc) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to clone and setup state");
|
||||
return NULL;
|
||||
|
||||
if (xfrm_init_state(xc) < 0)
|
||||
goto error;
|
||||
|
||||
/* configure the hardware if offload is requested */
|
||||
if (xuo && xfrm_dev_state_add(net, xc, xuo, extack))
|
||||
goto error;
|
||||
|
||||
/* add state */
|
||||
if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
|
||||
/* a care is needed when the destination address of the
|
||||
state is to be updated as it is a part of triplet */
|
||||
xfrm_state_insert(xc);
|
||||
} else {
|
||||
if (xfrm_state_add(xc) < 0)
|
||||
goto error_add;
|
||||
}
|
||||
|
||||
if (xfrm_init_state(xc, extack) < 0) {
|
||||
NL_SET_ERR_MSG_WEAK(extack, "Failed to initialize migrated state");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* configure the hardware if offload is requested */
|
||||
if (m->xuo && xfrm_dev_state_add(net, xc, m->xuo, extack))
|
||||
goto error;
|
||||
|
||||
return xc;
|
||||
error_add:
|
||||
if (xuo)
|
||||
xfrm_dev_state_delete(xc);
|
||||
error:
|
||||
xc->km.state = XFRM_STATE_DEAD;
|
||||
xfrm_state_put(xc);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_state_migrate_create);
|
||||
|
||||
int xfrm_state_migrate_install(const struct xfrm_state *x,
|
||||
struct xfrm_state *xc,
|
||||
const struct xfrm_migrate *m,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (m->new_family == m->old_family &&
|
||||
xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) {
|
||||
/*
|
||||
* Care is needed when the destination address of the state is
|
||||
* to be updated as it is a part of triplet.
|
||||
*/
|
||||
xfrm_state_insert(xc);
|
||||
} else {
|
||||
if (xfrm_state_add(xc) < 0) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to add migrated state");
|
||||
if (m->xuo)
|
||||
xfrm_dev_state_delete(xc);
|
||||
xc->km.state = XFRM_STATE_DEAD;
|
||||
xfrm_state_put(xc);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_state_migrate_install);
|
||||
|
||||
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
|
||||
struct xfrm_migrate *m,
|
||||
struct net *net,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct xfrm_state *xc;
|
||||
|
||||
xc = xfrm_state_migrate_create(x, m, net, extack);
|
||||
if (!xc)
|
||||
return NULL;
|
||||
|
||||
xfrm_migrate_sync(xc, x);
|
||||
|
||||
if (xfrm_state_migrate_install(x, xc, m, extack) < 0)
|
||||
return NULL;
|
||||
|
||||
return xc;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_state_migrate);
|
||||
#endif
|
||||
|
||||
@@ -3240,11 +3278,11 @@ error:
|
||||
|
||||
EXPORT_SYMBOL(__xfrm_init_state);
|
||||
|
||||
int xfrm_init_state(struct xfrm_state *x)
|
||||
int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __xfrm_init_state(x, NULL);
|
||||
err = __xfrm_init_state(x, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
+424
-66
@@ -248,6 +248,72 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_mtimer_thresh(bool has_encap, u8 dir,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (!has_encap) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"MTIMER_THRESH requires encapsulation");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dir == XFRM_SA_DIR_OUT) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"MTIMER_THRESH should not be set on output SA");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_xfrm_family(u16 family, struct netlink_ext_ack *extack)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
return 0;
|
||||
#else
|
||||
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
|
||||
return -EAFNOSUPPORT;
|
||||
#endif
|
||||
default:
|
||||
NL_SET_ERR_MSG(extack, "Invalid address family");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int verify_selector_prefixlen(u16 family,
|
||||
const struct xfrm_selector *sel,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_UNSPEC:
|
||||
return 0;
|
||||
case AF_INET:
|
||||
if (sel->prefixlen_d > 32 || sel->prefixlen_s > 32) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"Invalid prefix length in selector (must be <= 32 for IPv4)");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (sel->prefixlen_d > 128 || sel->prefixlen_s > 128) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"Invalid prefix length in selector (must be <= 128 for IPv6)");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
|
||||
return -EAFNOSUPPORT;
|
||||
#endif
|
||||
default:
|
||||
NL_SET_ERR_MSG(extack, "Invalid address family in selector");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
struct nlattr **attrs,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -256,58 +322,16 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
u8 sa_dir = nla_get_u8_default(attrs[XFRMA_SA_DIR], 0);
|
||||
u16 family = p->sel.family;
|
||||
|
||||
err = -EINVAL;
|
||||
switch (p->family) {
|
||||
case AF_INET:
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
break;
|
||||
#else
|
||||
err = -EAFNOSUPPORT;
|
||||
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
|
||||
err = verify_xfrm_family(p->family, extack);
|
||||
if (err)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
default:
|
||||
NL_SET_ERR_MSG(extack, "Invalid address family");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!family && !(p->flags & XFRM_STATE_AF_UNSPEC))
|
||||
family = p->family;
|
||||
|
||||
switch (family) {
|
||||
case AF_UNSPEC:
|
||||
break;
|
||||
|
||||
case AF_INET:
|
||||
if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
#else
|
||||
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
|
||||
err = -EAFNOSUPPORT;
|
||||
err = verify_selector_prefixlen(family, &p->sel, extack);
|
||||
if (err)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
default:
|
||||
NL_SET_ERR_MSG(extack, "Invalid address family in selector");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = -EINVAL;
|
||||
switch (p->id.proto) {
|
||||
@@ -455,18 +479,9 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
err = 0;
|
||||
|
||||
if (attrs[XFRMA_MTIMER_THRESH]) {
|
||||
if (!attrs[XFRMA_ENCAP]) {
|
||||
NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
|
||||
err = -EINVAL;
|
||||
err = verify_mtimer_thresh(!!attrs[XFRMA_ENCAP], sa_dir, extack);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sa_dir == XFRM_SA_DIR_OUT) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"MTIMER_THRESH attribute should not be set on output SA");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (sa_dir == XFRM_SA_DIR_OUT) {
|
||||
@@ -1186,6 +1201,16 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
|
||||
struct xfrm_user_offload *xuo)
|
||||
{
|
||||
xuo->ifindex = xso->dev->ifindex;
|
||||
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
|
||||
xuo->flags = XFRM_OFFLOAD_INBOUND;
|
||||
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
|
||||
xuo->flags |= XFRM_OFFLOAD_PACKET;
|
||||
}
|
||||
|
||||
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
|
||||
{
|
||||
struct xfrm_user_offload *xuo;
|
||||
@@ -1197,11 +1222,7 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
|
||||
|
||||
xuo = nla_data(attr);
|
||||
memset(xuo, 0, sizeof(*xuo));
|
||||
xuo->ifindex = xso->dev->ifindex;
|
||||
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
|
||||
xuo->flags = XFRM_OFFLOAD_INBOUND;
|
||||
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
|
||||
xuo->flags |= XFRM_OFFLOAD_PACKET;
|
||||
xso_to_xuo(xso, xuo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1326,7 +1347,7 @@ static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
|
||||
static int xfrm_smark_put(struct sk_buff *skb, const struct xfrm_mark *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@@ -3075,6 +3096,25 @@ nomem:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XFRM_MIGRATE
|
||||
static void copy_from_user_migrate_state(struct xfrm_migrate *ma,
|
||||
const struct xfrm_user_migrate_state *um)
|
||||
{
|
||||
memcpy(&ma->old_daddr, &um->id.daddr, sizeof(ma->old_daddr));
|
||||
memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
|
||||
memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
|
||||
|
||||
ma->proto = um->id.proto;
|
||||
ma->new_reqid = um->new_reqid;
|
||||
|
||||
ma->old_family = um->id.family;
|
||||
ma->new_family = um->new_family;
|
||||
|
||||
ma->old_mark = um->old_mark;
|
||||
ma->flags = um->flags;
|
||||
ma->new_sel = &um->new_sel;
|
||||
ma->msg_type = XFRM_MSG_MIGRATE_STATE;
|
||||
}
|
||||
|
||||
static int copy_from_user_migrate(struct xfrm_migrate *ma,
|
||||
struct xfrm_kmaddress *k,
|
||||
struct nlattr **attrs, int *num,
|
||||
@@ -3110,10 +3150,11 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
|
||||
|
||||
ma->proto = um->proto;
|
||||
ma->mode = um->mode;
|
||||
ma->reqid = um->reqid;
|
||||
ma->old_reqid = um->reqid;
|
||||
|
||||
ma->old_family = um->old_family;
|
||||
ma->new_family = um->new_family;
|
||||
ma->msg_type = XFRM_MSG_MIGRATE;
|
||||
}
|
||||
|
||||
*num = i;
|
||||
@@ -3124,7 +3165,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct nlattr **attrs, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
|
||||
struct xfrm_migrate m[XFRM_MAX_DEPTH];
|
||||
struct xfrm_migrate m[XFRM_MAX_DEPTH] = {};
|
||||
struct xfrm_kmaddress km, *kmp;
|
||||
u8 type;
|
||||
int err;
|
||||
@@ -3177,7 +3218,298 @@ error:
|
||||
kfree(xuo);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int build_migrate_state(struct sk_buff *skb,
|
||||
const struct xfrm_user_migrate_state *um,
|
||||
const struct xfrm_migrate *m,
|
||||
u8 dir, u32 portid, u32 seq)
|
||||
{
|
||||
int err;
|
||||
struct nlmsghdr *nlh;
|
||||
struct xfrm_user_migrate_state *hdr;
|
||||
|
||||
nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_MIGRATE_STATE,
|
||||
sizeof(struct xfrm_user_migrate_state), 0);
|
||||
if (!nlh)
|
||||
return -EMSGSIZE;
|
||||
|
||||
hdr = nlmsg_data(nlh);
|
||||
*hdr = *um;
|
||||
hdr->new_sel = *m->new_sel;
|
||||
|
||||
if (m->encap) {
|
||||
err = nla_put(skb, XFRMA_ENCAP, sizeof(*m->encap), m->encap);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (m->xuo) {
|
||||
err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*m->xuo), m->xuo);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (m->new_mark) {
|
||||
err = nla_put(skb, XFRMA_MARK, sizeof(*m->new_mark),
|
||||
m->new_mark);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
err = xfrm_smark_put(skb, &m->smark);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
|
||||
if (m->mapping_maxage) {
|
||||
err = nla_put_u32(skb, XFRMA_MTIMER_THRESH, m->mapping_maxage);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (m->nat_keepalive_interval) {
|
||||
err = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL,
|
||||
m->nat_keepalive_interval);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (dir) {
|
||||
err = nla_put_u8(skb, XFRMA_SA_DIR, dir);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
||||
out_cancel:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int xfrm_migrate_state_msgsize(const struct xfrm_migrate *m,
|
||||
u8 dir)
|
||||
{
|
||||
return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) +
|
||||
(m->encap ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0) +
|
||||
(m->xuo ? nla_total_size(sizeof(struct xfrm_user_offload)) : 0) +
|
||||
(m->new_mark ? nla_total_size(sizeof(struct xfrm_mark)) : 0) +
|
||||
((m->smark.v | m->smark.m) ? nla_total_size(sizeof(u32)) * 2 : 0) +
|
||||
(m->mapping_maxage ? nla_total_size(sizeof(u32)) : 0) +
|
||||
(m->nat_keepalive_interval ? nla_total_size(sizeof(u32)) : 0) +
|
||||
(dir ? nla_total_size(sizeof(u8)) : 0); /* XFRMA_SA_DIR */
|
||||
}
|
||||
|
||||
static int xfrm_send_migrate_state(struct net *net,
|
||||
const struct xfrm_user_migrate_state *um,
|
||||
const struct xfrm_migrate *m,
|
||||
u8 dir, u32 portid, u32 seq)
|
||||
{
|
||||
int err;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = nlmsg_new(xfrm_migrate_state_msgsize(m, dir), GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
err = build_migrate_state(skb, um, m, dir, portid, seq);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
|
||||
}
|
||||
|
||||
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct nlattr **attrs, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct xfrm_user_migrate_state *um = nlmsg_data(nlh);
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct xfrm_user_offload xuo = {};
|
||||
struct xfrm_migrate m = {};
|
||||
struct xfrm_state *xc;
|
||||
struct xfrm_state *x;
|
||||
int err;
|
||||
|
||||
if (!um->id.spi) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid SPI 0x0");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (um->reserved) {
|
||||
NL_SET_ERR_MSG(extack, "Reserved field must be zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS) {
|
||||
NL_SET_ERR_MSG_FMT(extack, "Unknown flags: 0x%x",
|
||||
um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = verify_xfrm_family(um->new_family, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)) {
|
||||
err = verify_selector_prefixlen(um->new_sel.family,
|
||||
&um->new_sel, extack);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
copy_from_user_migrate_state(&m, um);
|
||||
|
||||
x = xfrm_state_lookup(net, m.old_mark.v & m.old_mark.m,
|
||||
&um->id.daddr, um->id.spi,
|
||||
um->id.proto, um->id.family);
|
||||
if (!x) {
|
||||
NL_SET_ERR_MSG(extack, "Can not find state");
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
if (um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) {
|
||||
u8 prefixlen = (x->props.family == AF_INET6) ? 128 : 32;
|
||||
|
||||
if (x->sel.prefixlen_s != x->sel.prefixlen_d ||
|
||||
x->sel.prefixlen_d != prefixlen ||
|
||||
!xfrm_addr_equal(&x->sel.daddr, &x->id.daddr, x->props.family) ||
|
||||
!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family)) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"SA selector is not a single-host match for SA addresses");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs[XFRMA_ENCAP]) {
|
||||
m.encap = nla_data(attrs[XFRMA_ENCAP]);
|
||||
if (m.encap->encap_type == 0) {
|
||||
m.encap = NULL; /* sentinel: remove encap */
|
||||
} else if (m.encap->encap_type != UDP_ENCAP_ESPINUDP) {
|
||||
NL_SET_ERR_MSG(extack, "Unsupported encapsulation type");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
m.encap = x->encap; /* omit-to-inherit */
|
||||
}
|
||||
|
||||
if (attrs[XFRMA_MTIMER_THRESH]) {
|
||||
err = verify_mtimer_thresh(!!m.encap, x->dir, extack);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL], 0) && !m.encap) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"NAT_KEEPALIVE_INTERVAL requires encapsulation");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (attrs[XFRMA_OFFLOAD_DEV]) {
|
||||
m.xuo = nla_data(attrs[XFRMA_OFFLOAD_DEV]);
|
||||
} else {
|
||||
bool inherit_offload = !(um->flags & XFRM_MIGRATE_STATE_CLEAR_OFFLOAD);
|
||||
|
||||
if (inherit_offload && x->xso.dev) {
|
||||
xso_to_xuo(&x->xso, &xuo);
|
||||
m.xuo = &xuo;
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs[XFRMA_MARK])
|
||||
m.new_mark = nla_data(attrs[XFRMA_MARK]);
|
||||
|
||||
if (attrs[XFRMA_SET_MARK])
|
||||
xfrm_smark_init(attrs, &m.smark);
|
||||
else
|
||||
m.smark = x->props.smark;
|
||||
|
||||
m.mapping_maxage = nla_get_u32_default(attrs[XFRMA_MTIMER_THRESH],
|
||||
x->mapping_maxage);
|
||||
m.nat_keepalive_interval = nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL],
|
||||
x->nat_keepalive_interval);
|
||||
|
||||
if (m.new_family != um->id.family ||
|
||||
!xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family)) {
|
||||
u32 new_mark_key = m.new_mark ? m.new_mark->v & m.new_mark->m :
|
||||
m.old_mark.v & m.old_mark.m;
|
||||
struct xfrm_state *x_new;
|
||||
|
||||
x_new = xfrm_state_lookup(net, new_mark_key, &m.new_daddr,
|
||||
um->id.spi, um->id.proto, m.new_family);
|
||||
if (x_new) {
|
||||
xfrm_state_put(x_new);
|
||||
NL_SET_ERR_MSG(extack, "New SA tuple already occupied");
|
||||
err = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
xc = xfrm_state_migrate_create(x, &m, net, extack);
|
||||
if (!xc) {
|
||||
NL_SET_ERR_MSG_WEAK(extack, "State migration clone failed");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_bh(&x->lock);
|
||||
if (x->km.state != XFRM_STATE_VALID) {
|
||||
spin_unlock_bh(&x->lock);
|
||||
NL_SET_ERR_MSG(extack, "State already deleted");
|
||||
err = -ESRCH;
|
||||
goto out_xc;
|
||||
}
|
||||
xfrm_migrate_sync(xc, x); /* to prevent SN/IV reuse */
|
||||
__xfrm_state_delete(x);
|
||||
spin_unlock_bh(&x->lock);
|
||||
|
||||
err = xfrm_state_migrate_install(x, xc, &m, extack);
|
||||
if (err < 0) {
|
||||
/*
|
||||
* Should not occur: pre-check above ensures the new tuple is
|
||||
* free under xfrm_cfg_mutex. Both SAs are gone if it does;
|
||||
* restoring x would risk SN/IV reuse.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Restore encap cleared by sentinel (type=0) during migration. */
|
||||
if (attrs[XFRMA_ENCAP])
|
||||
m.encap = nla_data(attrs[XFRMA_ENCAP]);
|
||||
|
||||
m.new_sel = &xc->sel;
|
||||
m.mapping_maxage = xc->mapping_maxage;
|
||||
m.nat_keepalive_interval = xc->nat_keepalive_interval;
|
||||
|
||||
err = xfrm_send_migrate_state(net, um, &m, xc->dir,
|
||||
nlh->nlmsg_pid, nlh->nlmsg_seq);
|
||||
if (err < 0) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to send migration notification");
|
||||
err = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
xfrm_state_put(x);
|
||||
return err;
|
||||
out_xc:
|
||||
xc->km.state = XFRM_STATE_DEAD;
|
||||
xfrm_state_put(xc);
|
||||
xfrm_state_put(x);
|
||||
return err;
|
||||
}
|
||||
|
||||
#else
|
||||
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct nlattr **attrs, struct netlink_ext_ack *extack)
|
||||
{
|
||||
NL_SET_ERR_MSG(extack, "XFRM_MSG_MIGRATE_STATE is not supported");
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
||||
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct nlattr **attrs, struct netlink_ext_ack *extack)
|
||||
{
|
||||
@@ -3193,7 +3525,7 @@ static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *sk
|
||||
memset(&um, 0, sizeof(um));
|
||||
um.proto = m->proto;
|
||||
um.mode = m->mode;
|
||||
um.reqid = m->reqid;
|
||||
um.reqid = m->old_reqid;
|
||||
um.old_family = m->old_family;
|
||||
memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr));
|
||||
memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr));
|
||||
@@ -3330,6 +3662,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
|
||||
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
|
||||
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
|
||||
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
|
||||
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(xfrm_msg_min);
|
||||
|
||||
@@ -3423,6 +3756,7 @@ static const struct xfrm_link {
|
||||
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
|
||||
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
|
||||
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
|
||||
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate_state },
|
||||
};
|
||||
|
||||
static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
|
||||
@@ -3454,6 +3788,30 @@ static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
|
||||
}
|
||||
}
|
||||
|
||||
if (type == XFRM_MSG_MIGRATE_STATE) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= XFRMA_MAX; i++) {
|
||||
if (!attrs[i])
|
||||
continue;
|
||||
|
||||
switch (i) {
|
||||
case XFRMA_MARK:
|
||||
case XFRMA_ENCAP:
|
||||
case XFRMA_OFFLOAD_DEV:
|
||||
case XFRMA_SET_MARK:
|
||||
case XFRMA_SET_MARK_MASK:
|
||||
case XFRMA_MTIMER_THRESH:
|
||||
case XFRMA_NAT_KEEPALIVE_INTERVAL:
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_ATTR(extack, attrs[i],
|
||||
"Unsupported attribute in XFRM_MSG_MIGRATE_STATE");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ static const struct nlmsg_perm nlmsg_xfrm_perms[] = {
|
||||
{ XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
{ XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ },
|
||||
{ XFRM_MSG_MIGRATE_STATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
|
||||
};
|
||||
|
||||
static const struct nlmsg_perm nlmsg_audit_perms[] = {
|
||||
@@ -203,7 +204,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
|
||||
* structures at the top of this file with the new mappings
|
||||
* before updating the BUILD_BUG_ON() macro!
|
||||
*/
|
||||
BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT);
|
||||
BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MIGRATE_STATE);
|
||||
|
||||
if (selinux_policycap_netlink_xperm()) {
|
||||
*perm = NETLINK_XFRM_SOCKET__NLMSG;
|
||||
|
||||
Reference in New Issue
Block a user