Merge tag 'ipsec-next-2026-06-12' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next
Steffen Klassert says: ==================== pull request (net-next): ipsec-next 2026-06-12 1) Replace the open-coded manual cleanup in xfrm_add_policy() error path with xfrm_policy_destroy() for consistency with xfrm_policy_construct(). From Deepanshu Kartikey. 2) Limit XFRMA_TFCPAD to a sensible maximum (max IP length, 64k) since u32 is excessive for traffic flow confidentiality padding. From David Ahern. 3) Add a new netlink message XFRM_MSG_MIGRATE_STATE that allows migrating individual IPsec SAs independently of their policies. The existing XFRM_MSG_MIGRATE is tightly coupled to policy+SA migration, lacks SPI for unique SA identification, and cannot express reqid changes or migrate Transport mode selectors. The new interface identifies the SA via SPI and mark, supports reqid changes, address family changes, encap removal, and uses an atomic create+install flow under x->lock to prevent SN/IV reuse during AEAD SA migration. From Antony Antony. * tag 'ipsec-next-2026-06-12' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next: xfrm: add documentation for XFRM_MSG_MIGRATE_STATE xfrm: restrict netlink attributes for XFRM_MSG_MIGRATE_STATE xfrm: add XFRM_MSG_MIGRATE_STATE for single SA migration xfrm: make xfrm_dev_state_add xuo parameter const xfrm: extract address family and selector validation helpers xfrm: refactor XFRMA_MTIMER_THRESH validation into a helper xfrm: move encap and xuo into struct xfrm_migrate xfrm: add error messages to state migration xfrm: add state synchronization after migration xfrm: check family before comparing addresses in migrate xfrm: split xfrm_state_migrate into create and install functions xfrm: rename reqid in xfrm_migrate xfrm: fix NAT-related field inheritance in SA migration xfrm: allow migration from UDP encapsulated to non-encapsulated ESP xfrm: add extack to xfrm_init_state xfrm: remove redundant assignments xfrm: Reject excessive values for XFRMA_TFCPAD xfrm: cleanup error path in xfrm_add_policy() ==================== Link: https://patch.msgid.link/20260612074725.1760473-1-steffen.klassert@secunet.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
431662b642
13 files changed
+927
-157
No files matched your search
@@ -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
|
||||
@@ -1775,7 +1785,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,
|
||||
@@ -1896,11 +1906,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,
|
||||
@@ -2014,23 +2030,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);
|
||||
@@ -2069,7 +2113,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,
|
||||
@@ -2140,7 +2184,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
@@ -4530,7 +4530,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:
|
||||
@@ -4624,7 +4624,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;
|
||||
@@ -4635,6 +4635,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,
|
||||
@@ -4672,7 +4687,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
|
||||
|
||||
@@ -3253,11 +3291,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;
|
||||
|
||||
|
||||
+433
-70
@@ -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) {
|
||||
@@ -937,8 +952,14 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
|
||||
attrs[XFRMA_ALG_COMP], extack)))
|
||||
goto error;
|
||||
|
||||
if (attrs[XFRMA_TFCPAD])
|
||||
if (attrs[XFRMA_TFCPAD]) {
|
||||
x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]);
|
||||
if (x->tfcpad > IP_MAX_MTU) {
|
||||
NL_SET_ERR_MSG(extack, "Excessive TFC padding");
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
xfrm_mark_get(attrs, &x->mark);
|
||||
|
||||
@@ -1180,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;
|
||||
@@ -1191,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;
|
||||
}
|
||||
@@ -1320,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;
|
||||
|
||||
@@ -2267,9 +2294,8 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
|
||||
if (err) {
|
||||
xfrm_dev_policy_delete(xp);
|
||||
xfrm_dev_policy_free(xp);
|
||||
security_xfrm_policy_free(xp->security);
|
||||
kfree(xp);
|
||||
xp->walk.dead = 1;
|
||||
xfrm_policy_destroy(xp);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -3070,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,
|
||||
@@ -3105,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;
|
||||
@@ -3119,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;
|
||||
@@ -3172,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)
|
||||
{
|
||||
@@ -3188,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));
|
||||
@@ -3325,6 +3662,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
|
||||
[XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping),
|
||||
[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);
|
||||
|
||||
@@ -3418,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,
|
||||
@@ -3449,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