* Re: [PATCH 0/7] Netfilter/IPVS fixes for net
From: David Miller @ 2019-01-28 18:52 UTC (permalink / raw)
To: pablo; +Cc: netfilter-devel, netdev
In-Reply-To: <20190128140405.15020-1-pablo@netfilter.org>
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon, 28 Jan 2019 15:03:58 +0100
> The following patchset contains Netfilter/IPVS fixes for your net tree:
>
> 1) The nftnl mutex is now per-netns, therefore use reference counter
> for matches and targets to deal with concurrent updates from netns.
> Moreover, place extensions in a pernet list. Patches from Florian Westphal.
>
> 2) Bail out with EINVAL in case of negative timeouts via setsockopt()
> through ip_vs_set_timeout(), from ZhangXiaoxu.
>
> 3) Spurious EINVAL on ebtables 32bit binary with 64bit kernel, also
> from Florian.
>
> 4) Reset TCP option header parser in case of fingerprint mismatch,
> otherwise follow up overlapping fingerprint definitions including
> TCP options do not work, from Fernando Fernandez Mancera.
>
> 5) Compilation warning in ipt_CLUSTER with CONFIG_PROC_FS unset.
> From Anders Roxell.
>
> You can pull these changes from:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
Pulled, thanks Pablo.
^ permalink raw reply
* Re: [oss-drivers] [PATCH bpf-next] tools: bpftool: warn about risky prog array updates
From: Quentin Monnet @ 2019-01-28 18:51 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Alexei Starovoitov, Daniel Borkmann, OSS-drivers Netronome,
Linux Kernel Network Developers
In-Reply-To: <20190128182915.434-1-jakub.kicinski@netronome.com>
On Mon, 28 Jan 2019 at 18:29, Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> When prog array is updated with bpftool users often refer
> to the map via the ID. Unfortunately, that's likely
> to lead to confusion because prog arrays get flushed when
> the last user reference is gone. If there is no other
> reference bpftool will create one, update successfully
> just to close the map again and have it flushed.
>
> Warn about this case in non-JSON mode.
>
> If the problem continues causing confusion we can remove
> the support for referring to a map by ID for prog array
> update completely. For now it seems like the potential
> inconvenience to users who know what they're doing outweighs
> the benefit.
>
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
All good for me!
Quentin
^ permalink raw reply
* Re: [PATCH net-next 0/7] mlxsw: Misc updates
From: David Miller @ 2019-01-28 18:43 UTC (permalink / raw)
To: idosch; +Cc: netdev, jiri, mlxsw
In-Reply-To: <20190128120131.2848-1-idosch@mellanox.com>
From: Ido Schimmel <idosch@mellanox.com>
Date: Mon, 28 Jan 2019 12:02:06 +0000
> This patchset contains miscellaneous patches we gathered in our queue.
> Some of them are dependencies of larger patchsets that I will submit
> later this cycle.
>
> Patches #1-#3 perform small non-functional changes in mlxsw.
>
> Patch #4 adds more extended ack messages in mlxsw.
>
> Patch #5 adds devlink parameters documentation for mlxsw. To be extended
> with more parameters this cycle.
>
> Patches #6-#7 perform small changes in forwarding selftests
> infrastructure.
Series applied.
^ permalink raw reply
* Re: [PATCH net-next 00/24] sctp: support SCTP_FUTURE/CURRENT/ALL_ASSOC
From: David Miller @ 2019-01-28 18:41 UTC (permalink / raw)
To: nhorman; +Cc: lucien.xin, netdev, linux-sctp, marcelo.leitner
In-Reply-To: <20190128094444.GA1624@neilslaptop.think-freely.org>
From: Neil Horman <nhorman@tuxdriver.com>
Date: Mon, 28 Jan 2019 04:44:44 -0500
> Sorry, I'm traveling at the moment, but I'll review this as soon as
> I'm back on the ground
I'll hold this until you get a chance to review Neil.
^ permalink raw reply
* Want retouching for your photos?
From: Jennifer @ 2019-01-28 7:54 UTC (permalink / raw)
To: netdev
Did you receive my email from last week?
Do you have needs for retouching of your photos? We can do it white
background for your pictures.
If you need clipping path just let me know.
You can send 1 or 2 photos for testing to judge our quality.
Thanks,
Jennifer
^ permalink raw reply
* Want retouching for your photos?
From: Jennifer @ 2019-01-28 9:47 UTC (permalink / raw)
To: netdev
Did you receive my email from last week?
Do you have needs for retouching of your photos? We can do it white
background for your pictures.
If you need clipping path just let me know.
You can send 1 or 2 photos for testing to judge our quality.
Thanks,
Jennifer
^ permalink raw reply
* Want retouching for your photos?
From: Jennifer @ 2019-01-28 8:00 UTC (permalink / raw)
To: netdev
Did you receive my email from last week?
Do you have needs for retouching of your photos? We can do it white
background for your pictures.
If you need clipping path just let me know.
You can send 1 or 2 photos for testing to judge our quality.
Thanks,
Jennifer
^ permalink raw reply
* [PATCH bpf-next] tools: bpftool: warn about risky prog array updates
From: Jakub Kicinski @ 2019-01-28 18:29 UTC (permalink / raw)
To: alexei.starovoitov, daniel; +Cc: oss-drivers, netdev, Jakub Kicinski
When prog array is updated with bpftool users often refer
to the map via the ID. Unfortunately, that's likely
to lead to confusion because prog arrays get flushed when
the last user reference is gone. If there is no other
reference bpftool will create one, update successfully
just to close the map again and have it flushed.
Warn about this case in non-JSON mode.
If the problem continues causing confusion we can remove
the support for referring to a map by ID for prog array
update completely. For now it seems like the potential
inconvenience to users who know what they're doing outweighs
the benefit.
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
---
tools/bpf/bpftool/map.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 8cb0e26907ff..6f33818bb6b6 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -426,6 +426,9 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
p_err("not enough value arguments for map of progs");
return -1;
}
+ if (is_prefix(*argv, "id"))
+ p_info("Warning: updating program array via MAP_ID, make sure this map is kept open\n"
+ " by some process or pinned otherwise update will be lost");
fd = prog_parse_fd(&argc, &argv);
if (fd < 0)
--
2.19.2
^ permalink raw reply related
* Re: r8169 Driver - Poor Network Performance Since Kernel 4.19
From: Heiner Kallweit @ 2019-01-28 18:28 UTC (permalink / raw)
To: Peter Ceiley, Realtek linux nic maintainers; +Cc: netdev
In-Reply-To: <CAMLO_R7Q6QCr7KX5goOvJ_BzDLU_tnw3UkMKzwDmL4C2BHubVA@mail.gmail.com>
On 28.01.2019 12:13, Peter Ceiley wrote:
> Hi,
>
> I have been experiencing very poor network performance since Kernel
> 4.19 and I'm confident it's related to the r8169 driver.
>
> I have no issue with kernel versions 4.18 and prior. I am experiencing
> this issue in kernels 4.19 and 4.20 (currently running/testing with
> 4.20.4 & 4.19.18).
>
> If someone could guide me in the right direction, I'm happy to help
> troubleshoot this issue. Note that I have been keeping an eye on one
> issue related to loading of the PHY driver, however, my symptoms
> differ in that I still have a network connection. I have attempted to
> reload the driver on a running system, but this does not improve the
> situation.
>
> Using the proprietary r8168 driver returns my device to proper working order.
>
> lshw shows:
> description: Ethernet interface
> product: RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
> vendor: Realtek Semiconductor Co., Ltd.
> physical id: 0
> bus info: pci@0000:03:00.0
> logical name: enp3s0
> version: 0c
> serial:
> size: 1Gbit/s
> capacity: 1Gbit/s
> width: 64 bits
> clock: 33MHz
> capabilities: pm msi pciexpress msix vpd bus_master cap_list
> ethernet physical tp aui bnc mii fibre 10bt 10bt-fd 100bt 100bt-fd
> 1000bt-fd autonegotiation
> configuration: autonegotiation=on broadcast=yes driver=r8169
> duplex=full firmware=rtl8168g-2_0.0.1 02/06/13 ip=192.168.1.25
> latency=0 link=yes multicast=yes port=MII speed=1Gbit/s
> resources: irq:19 ioport:d000(size=256)
> memory:f7b00000-f7b00fff memory:f2100000-f2103fff
>
> Kind Regards,
>
> Peter.
>
Hi Peter,
the description "poor network performance" is quite vague, therefore:
- Can you provide any measurements?
- iperf results before and after
- statistics about dropped packets (rx and/or tx)
- Do you use jumbo packets?
Also help would be a "lspci -vv" output for the network card and
the dmesg output line with the chip XID.
Heiner
^ permalink raw reply
* Re: [PATCH v3 lora-next 4/4] dt-bindings: lora: sx130x: add clock bindings
From: Rob Herring @ 2019-01-28 18:23 UTC (permalink / raw)
To: Ben Whitten
Cc: Andreas Färber, David Miller, Mark Rutland, netdev,
devicetree, linux-kernel@vger.kernel.org, linux-lpwan
In-Reply-To: <20190124145309.21698-5-ben.whitten@lairdtech.com>
On Thu, Jan 24, 2019 at 8:54 AM Ben Whitten <ben.whitten@gmail.com> wrote:
>
> From: Ben Whitten <ben.whitten@gmail.com>
>
> The SX130x family consumes two clocks, a 32 MHz clock provided by a
> connected IQ transceiver, and a 133 MHz high speed clock.
> It also provides a 32 MHz clock out to the radio in the TX path.
>
> In the example we connect the concentrator to a fixed clock providing
> the 133 MHz high speed clock, and we connect to a connected transceiver
> 32 MHz clock.
>
> A radio in the TX path is also connected to the 32 MHz clock out from the
> concentrator.
>
> The connected radios are both fed from a fixed 32 MHz clock, with only
> one being the clock source back to the SX130x concentrator.
>
> Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
> ---
> v2 -> v3:
> * Name input clocks specifically
> * Add output clock for radio in TX path
> v1 -> v2:
> * Fixed incorrect usage of clock cells
> * Fixed wording in commit and descriptions
> ---
> .../bindings/net/lora/semtech,sx130x.yaml | 52 ++++++++++++++++++-
> 1 file changed, 51 insertions(+), 1 deletion(-)
Other than the issue on patch 3 that affects the example here,
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH v2 net-next 5/5] ip6tlvs: API to set and remove individual TLVs from DO or HBH EH
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net>
Add functions and socket options that allows setting and removing
individual TLVs from Hop-by-Hop, Destination, or Routing Header
Destination options that are set in txoptions of a socket. When an
individual TLV optiosn is set it is merged into the existing options
at the position in the list described by preferred order attribute in
the TLV parameters table.
This code is based in part on the TLV option handling in calipso.c.
Signed-off-by: Tom Herbert <tom@quantonium.net>
---
include/net/ipv6.h | 13 ++
include/uapi/linux/in6.h | 9 +
net/ipv6/exthdrs_options.c | 516 +++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ipv6_sockglue.c | 80 +++++++
4 files changed, 618 insertions(+)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index c5692d6..898f5e3 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -378,6 +378,8 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_opt_hdr *newopt);
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
+int ipv6_opt_update(struct sock *sk, struct ipv6_txoptions *opt,
+ int which, struct ipv6_opt_hdr *new);
int ipv6_opt_validate_tlvs(struct net *net, struct ipv6_opt_hdr *opt,
unsigned int optname, bool admin);
@@ -386,6 +388,17 @@ int ipv6_opt_validate_single_tlv(struct net *net, unsigned int optname,
bool deleting, bool admin);
int ipv6_opt_check_perm(struct net *net, struct sock *sk,
int optname, bool admin);
+
+int ipv6_opt_tlv_find(struct ipv6_opt_hdr *opt, unsigned char *targ_tlv,
+ unsigned int *start, unsigned int *end);
+struct ipv6_opt_hdr *ipv6_opt_tlv_insert(struct net *net,
+ struct ipv6_opt_hdr *opt,
+ int optname, unsigned char *tlv,
+ bool admin);
+struct ipv6_opt_hdr *ipv6_opt_tlv_delete(struct net *net,
+ struct ipv6_opt_hdr *opt,
+ unsigned char *tlv, bool admin);
+
struct tlv_tx_param {
unsigned char preferred_order;
unsigned char admin_perm : 2;
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index a54cf96..f6edf31 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -288,6 +288,15 @@ struct in6_flowlabel_req {
#define IPV6_RECVFRAGSIZE 77
#define IPV6_FREEBIND 78
+/* API to set single Destination or Hop-by-Hop options */
+
+#define IPV6_HOPOPTS_TLV 79
+#define IPV6_RTHDRDSTOPTS_TLV 80
+#define IPV6_DSTOPTS_TLV 81
+#define IPV6_HOPOPTS_DEL_TLV 82
+#define IPV6_RTHDRDSTOPTS_DEL_TLV 83
+#define IPV6_DSTOPTS_DEL_TLV 84
+
/*
* Multicast Routing:
* see include/uapi/linux/mroute6.h.
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index f334af5..673cd31 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -162,6 +162,35 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
}
EXPORT_SYMBOL_GPL(ipv6_fixup_options);
+/**
+ * ipv6_opt_update - Replaces socket's options with a new set
+ * @sk: the socket
+ * @opt: TX options from socket
+ * @which: which set of options
+ * @new: new extension header for the options
+ *
+ * Description:
+ * Replaces @sk's options with @new for type @which. @new may be NULL to
+ * leave the socket with no options for the given type.
+ *
+ */
+int ipv6_opt_update(struct sock *sk, struct ipv6_txoptions *opt,
+ int which, struct ipv6_opt_hdr *new)
+{
+ opt = ipv6_renew_options(sk, opt, which, new);
+ if (IS_ERR(opt))
+ return PTR_ERR(opt);
+
+ opt = ipv6_update_options(sk, opt);
+ if (opt) {
+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+ txopt_put(opt);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipv6_opt_update);
+
/* TLV validation functions */
/* Validate a single non-padding TLV */
@@ -460,6 +489,493 @@ int ipv6_opt_check_perm(struct net *net, struct sock *sk, int optname,
}
EXPORT_SYMBOL(ipv6_opt_check_perm);
+/* Functions to manage individual TLVs */
+
+/**
+ * __ipv6_opt_tlv_find - Finds a particular TLV in an IPv6 options header
+ * (destinaton or hop-by-hop options). If TLV is not present, then the
+ * preferred insertion point is determined.
+ * @opt: the options header (an EH header followed by data)
+ * @targ_tlv: Prototype of TLV to find
+ * @start: on return holds the offset of any leading padding if option
+ * is present, or offset at which option is inserted.
+ * @end: on return holds the offset of the first non-pad TLV after option
+ * if the option was found, else points to the first TLV after
+ * padding at intsertion point.
+ *
+ * Description:
+ * Finds the space occupied by particular option (including any leading and
+ * trailing padding), or the perferred position for insertion if the
+ * TLV is not present.
+ *
+ * If the option is found then @start and @end are set to the offsets within
+ * @opt of the start of padding before the first found option and the end of
+ * padding after the first found option. In this case the function returns
+ * the offset in @opt of the found option (a value >= 2 since the TLV
+ * must be after the option header).
+ *
+ * In the absence of the searched option, @start is set to offset in @opt at
+ * which the option may be inserted per the ordering and alignment rules
+ * in the TLV parameter table, and @end is set to the end + 1 of any
+ * padding at the @start offset. When the option is not found -ENOENT is
+ * returned.
+ *
+ * rcu_read_lock assumed held.
+ */
+static int __ipv6_opt_tlv_find(struct ipv6_opt_hdr *opt,
+ unsigned char *targ_tlv,
+ unsigned int *start, unsigned int *end)
+{
+ unsigned int offset_s = 0, offset_e = 0, last_s = 0;
+ unsigned char *tlv = (unsigned char *)opt;
+ unsigned int pad_e = sizeof(*opt);
+ int ret_val = -ENOENT, tlv_len;
+ unsigned int opt_len, offset;
+ struct tlv_tx_param *tptx;
+ unsigned int targ_order;
+ bool found_cand = false;
+
+ opt_len = ipv6_optlen(opt);
+ offset = sizeof(*opt);
+
+ tptx = tlv_deref_tx_params(targ_tlv[0]);
+
+ targ_order = tptx->preferred_order;
+
+ while (offset < opt_len) {
+ switch (tlv[offset]) {
+ case IPV6_TLV_PAD1:
+ if (offset_e)
+ offset_e = offset;
+ tlv_len = 1;
+ break;
+ case IPV6_TLV_PADN:
+ if (offset_e)
+ offset_e = offset;
+ tlv_len = tlv[offset + 1] + 2;
+ break;
+ default:
+ if (ret_val >= 0)
+ goto out;
+
+ /* Not found yet */
+
+ if (tlv[offset] == targ_tlv[0]) {
+ /* Found it */
+
+ ret_val = offset;
+ offset_e = offset;
+ offset_s = last_s;
+ found_cand = true;
+ } else {
+ struct tlv_tx_param *tptx1;
+
+ tptx1 = tlv_deref_tx_params(tlv[offset]);
+
+ if (targ_order < tptx1->preferred_order &&
+ !found_cand) {
+ /* Found candidate for insert location
+ */
+
+ pad_e = offset;
+ offset_s = last_s;
+ found_cand = true;
+ }
+ }
+
+ last_s = offset;
+ tlv_len = tlv[offset + 1] + 2;
+ break;
+ }
+
+ offset += tlv_len;
+ }
+
+ if (!found_cand) {
+ /* Not found and insert point is after all options */
+ offset_s = last_s;
+ pad_e = opt_len;
+ }
+
+out:
+ if (offset_s)
+ *start = offset_s +
+ (tlv[offset_s] ? tlv[offset_s + 1] + 2 : 1);
+ else
+ *start = sizeof(*opt);
+
+ if (ret_val >= 0)
+ *end = offset_e +
+ (tlv[offset_e] ? tlv[offset_e + 1] + 2 : 1);
+ else
+ *end = pad_e;
+
+ return ret_val;
+}
+
+int ipv6_opt_tlv_find(struct ipv6_opt_hdr *opt, unsigned char *targ_tlv,
+ unsigned int *start, unsigned int *end)
+{
+ int ret;
+
+ rcu_read_lock();
+ ret = __ipv6_opt_tlv_find(opt, targ_tlv, start, end);
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ipv6_opt_tlv_find);
+
+/**
+ * ipv6_opt_tlv_pad_write - Writes pad bytes in TLV format
+ * @buf: the buffer
+ * @offset: offset from start of buffer to write padding
+ * @count: number of pad bytes to write
+ *
+ * Description:
+ * Write @count bytes of TLV padding into @buffer starting at offset @offset.
+ * @count should be less than 8 - see RFC 4942.
+ *
+ */
+static int ipv6_opt_tlv_pad_write(unsigned char *buf, unsigned int offset,
+ unsigned int count)
+{
+ if (WARN_ON_ONCE(count >= 8))
+ return -EINVAL;
+
+ switch (count) {
+ case 0:
+ break;
+ case 1:
+ buf[offset] = IPV6_TLV_PAD1;
+ break;
+ default:
+ buf[offset] = IPV6_TLV_PADN;
+ buf[offset + 1] = count - 2;
+ if (count > 2)
+ memset(buf + offset + 2, 0, count - 2);
+ break;
+ }
+ return 0;
+}
+
+static unsigned int compute_padding(unsigned int offset, unsigned int mult,
+ unsigned int moff)
+{
+ return (mult - ((offset - moff) % mult)) % mult;
+}
+
+static int tlv_find_next(unsigned char *tlv, unsigned int offset,
+ unsigned int optlen)
+{
+ while (offset < optlen) {
+ switch (tlv[offset]) {
+ case IPV6_TLV_PAD1:
+ offset++;
+ break;
+ case IPV6_TLV_PADN:
+ offset += tlv[offset + 1] + 2;
+ break;
+ default:
+ return offset;
+ }
+ }
+
+ return (optlen);
+}
+
+/* __tlv_sum_alignment assumes ruc_read_lock is held */
+static size_t __tlv_sum_alignment(unsigned char *tlv, unsigned int offset,
+ unsigned int optlen)
+{
+ int sum = 0;
+
+ offset = tlv_find_next(tlv, offset, optlen);
+
+ while (offset < optlen) {
+ struct tlv_tx_param *tptx;
+
+ tptx = tlv_deref_tx_params(tlv[offset]);
+ sum += tptx->align_mult;
+ offset += tlv[offset + 1] + 2;
+ offset = tlv_find_next(tlv, offset, optlen);
+ }
+
+ return sum;
+}
+
+/* __copy_and_align_tlvs assumes ruc_read_lock is held */
+static int __copy_and_align_tlvs(unsigned int src_off, unsigned char *src,
+ unsigned int dst_off, unsigned char *dst,
+ unsigned int optlen)
+{
+ unsigned int padding, len;
+ struct tlv_tx_param *tptx;
+
+ if (!src)
+ return dst_off;
+
+ src_off = tlv_find_next(src, src_off, optlen);
+
+ while (src_off < optlen) {
+ tptx = tlv_deref_tx_params(src[src_off]);
+
+ padding = compute_padding(dst_off, tptx->align_mult + 1,
+ tptx->align_off);
+ ipv6_opt_tlv_pad_write(dst, dst_off, padding);
+ dst_off += padding;
+
+ len = src[src_off + 1] + 2;
+ memcpy(&dst[dst_off], &src[src_off], len);
+
+ src_off += len;
+ dst_off += len;
+ src_off = tlv_find_next(src, src_off, optlen);
+ }
+
+ return dst_off;
+}
+
+static int count_tlvs(struct ipv6_opt_hdr *opt)
+{
+ unsigned char *tlv = (unsigned char *)opt;
+ unsigned int opt_len, tlv_len, offset, cnt = 0;
+
+ opt_len = ipv6_optlen(opt);
+ offset = sizeof(*opt);
+
+ while (offset < opt_len) {
+ switch (tlv[offset]) {
+ case IPV6_TLV_PAD1:
+ tlv_len = 1;
+ break;
+ case IPV6_TLV_PADN:
+ tlv_len = tlv[offset + 1] + 2;
+ break;
+ default:
+ cnt++;
+ tlv_len = tlv[offset + 1] + 2;
+ break;
+ }
+ offset += tlv_len;
+ }
+
+ return cnt;
+}
+
+#define IPV6_OPT_MAX_END_PAD 7
+
+/**
+ * ipv6_opt_tlv_insert - Inserts a TLV into an IPv6 destination options
+ * or Hop-by-Hop options extension header.
+ *
+ * @net: Current net
+ * @opt: the original options extensions header
+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS
+ * @tlv: the new TLV being inserted
+ * @admin: Set for privileged user
+ *
+ * Description:
+ * Creates a new options header based on @opt with the specified option
+ * in @tlv option added to it. If @opt already contains the same type
+ * of TLV, then the TLV is overwritten, otherwise the new TLV is appended
+ * after any existing TLVs. If @opt is NULL then the new header
+ * will contain just the new option and any needed padding.
+ *
+ * Assumes option has been validated.
+ */
+struct ipv6_opt_hdr *ipv6_opt_tlv_insert(struct net *net,
+ struct ipv6_opt_hdr *opt,
+ int optname, unsigned char *tlv,
+ bool admin)
+{
+ unsigned int start = 0, end = 0, buf_len, pad, optlen, max_align;
+ size_t tlv_len = tlv[1] + 2;
+ struct tlv_tx_param *tptx;
+ struct ipv6_opt_hdr *new;
+ int ret_val;
+ u8 perm;
+
+ rcu_read_lock();
+
+ if (opt) {
+ optlen = ipv6_optlen(opt);
+ ret_val = __ipv6_opt_tlv_find(opt, tlv, &start, &end);
+ if (ret_val < 0) {
+ if (ret_val != -ENOENT) {
+ rcu_read_unlock();
+ return ERR_PTR(ret_val);
+ }
+ } else if (((unsigned char *)opt)[ret_val + 1] == tlv[1]) {
+ unsigned int roff = ret_val + tlv[1] + 2;
+
+ /* Replace existing TLV with one of the same length,
+ * we can fast path this.
+ */
+
+ rcu_read_unlock();
+
+ new = kmalloc(optlen, GFP_ATOMIC);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy((unsigned char *)new,
+ (unsigned char *)opt, ret_val);
+ memcpy((unsigned char *)new + ret_val, tlv, tlv[1] + 2);
+ memcpy((unsigned char *)new + roff,
+ (unsigned char *)opt + roff, optlen - roff);
+
+ return new;
+ }
+ } else {
+ optlen = 0;
+ start = sizeof(*opt);
+ end = 0;
+ }
+
+ tptx = tlv_deref_tx_params(tlv[0]);
+
+ /* Maximum buffer size we'll need including possible padding */
+ max_align = __tlv_sum_alignment((unsigned char *)opt, end, optlen) +
+ tptx->align_mult + IPV6_OPT_MAX_END_PAD;
+
+ buf_len = optlen + start - end + tlv_len + max_align;
+ new = kmalloc(buf_len, GFP_ATOMIC);
+ if (!new) {
+ rcu_read_unlock();
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf_len = start;
+
+ if (start > sizeof(*opt))
+ memcpy(new, opt, start);
+
+ pad = compute_padding(start, tptx->align_mult + 1, tptx->align_off);
+ ipv6_opt_tlv_pad_write((__u8 *)new, start, pad);
+ buf_len += pad;
+
+ memcpy((__u8 *)new + buf_len, tlv, tlv_len);
+ buf_len += tlv_len;
+
+ buf_len = __copy_and_align_tlvs(end, (__u8 *)opt, buf_len,
+ (__u8 *)new, optlen);
+
+ perm = admin ? tptx->admin_perm : tptx->user_perm;
+
+ rcu_read_unlock();
+
+ /* Trailer pad to 8 byte alignment */
+ pad = (8 - (buf_len & 7)) & 7;
+ ipv6_opt_tlv_pad_write((__u8 *)new, buf_len, pad);
+ buf_len += pad;
+
+ /* Set header */
+ new->nexthdr = 0;
+ new->hdrlen = buf_len / 8 - 1;
+
+ if (perm != IPV6_TLV_PERM_NO_CHECK) {
+ switch (optname) {
+ case IPV6_HOPOPTS:
+ if (buf_len > net->ipv6.sysctl.max_hbh_opts_len)
+ return ERR_PTR(-EMSGSIZE);
+ if (count_tlvs(new) > net->ipv6.sysctl.max_hbh_opts_cnt)
+ return ERR_PTR(-E2BIG);
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_DSTOPTS:
+ if (buf_len > net->ipv6.sysctl.max_dst_opts_len)
+ return ERR_PTR(-EMSGSIZE);
+ if (count_tlvs(new) > net->ipv6.sysctl.max_dst_opts_cnt)
+ return ERR_PTR(-E2BIG);
+ break;
+ }
+ }
+
+ return new;
+}
+EXPORT_SYMBOL(ipv6_opt_tlv_insert);
+
+/* rcu_read_lock assume held */
+struct ipv6_opt_hdr *__ipv6_opt_tlv_delete(struct ipv6_opt_hdr *opt,
+ unsigned int start,
+ unsigned int end)
+{
+ unsigned int pad, optlen, buf_len;
+ struct ipv6_opt_hdr *new;
+ size_t max_align;
+
+ optlen = ipv6_optlen(opt);
+ if (start == sizeof(*opt) && end == optlen) {
+ /* There's no other option in the header so return NULL */
+ return NULL;
+ }
+
+ max_align = __tlv_sum_alignment((unsigned char *)opt, end, optlen) +
+ IPV6_OPT_MAX_END_PAD;
+
+ new = kmalloc(optlen - (end - start) + max_align, GFP_ATOMIC);
+ if (!new)
+ return ERR_PTR(-ENOMEM); /* DIFF */
+
+ memcpy(new, opt, start);
+
+ buf_len = __copy_and_align_tlvs(end, (__u8 *)opt, start,
+ (__u8 *)new, optlen);
+
+ /* Now set trailer padding, buf_len is at the end of the last TLV at
+ * this point
+ */
+ pad = (8 - (buf_len & 7)) & 7;
+ ipv6_opt_tlv_pad_write((__u8 *)new, buf_len, pad);
+ buf_len += pad;
+
+ /* Set new header length */
+ new->hdrlen = buf_len / 8 - 1;
+
+ return new;
+}
+
+/**
+ * ipv6_opt_tlv_delete - Removes the specified option from the destination
+ * or Hop-by-Hop extension header.
+ * @net: Current net
+ * @opt: The original header
+ * @tlv: Prototype of TLV being removed
+ * @admin: Set for privileged user
+ *
+ * Description:
+ * Creates a new header based on @opt without the specified option in
+ * @tlv. A new options header is returned without the option. If @opt
+ * doesn't contain the specified option ERR_PTR(-ENOENT) is returned.
+ * If @opt contains no other non-padding options, NULL is returned.
+ * Otherwise, a new header is created and returned without the option
+ * (and removing as much padding as possible).
+ */
+struct ipv6_opt_hdr *ipv6_opt_tlv_delete(struct net *net,
+ struct ipv6_opt_hdr *opt,
+ unsigned char *tlv, bool admin)
+{
+ struct ipv6_opt_hdr *retopt;
+ unsigned int start, end;
+ int ret_val;
+
+ rcu_read_lock();
+
+ ret_val = __ipv6_opt_tlv_find(opt, tlv, &start, &end);
+ if (ret_val < 0) {
+ rcu_read_unlock();
+ return ERR_PTR(ret_val);
+ }
+
+ retopt = __ipv6_opt_tlv_delete(opt, start, end);
+
+ rcu_read_unlock();
+
+ return retopt;
+}
+EXPORT_SYMBOL(ipv6_opt_tlv_delete);
+
/* Destination options header */
#if IS_ENABLED(CONFIG_IPV6_MIP6)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 009c8a4..affa46c 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -493,6 +493,86 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
break;
}
+ case IPV6_HOPOPTS_TLV:
+ case IPV6_RTHDRDSTOPTS_TLV:
+ case IPV6_DSTOPTS_TLV:
+ case IPV6_HOPOPTS_DEL_TLV:
+ case IPV6_RTHDRDSTOPTS_DEL_TLV:
+ case IPV6_DSTOPTS_DEL_TLV:
+ {
+ struct ipv6_opt_hdr *old = NULL, *new = NULL;
+ struct ipv6_txoptions *opt;
+ bool deleting = false;
+ void *new_opt = NULL;
+ int which = -1;
+ bool admin;
+
+ new_opt = memdup_user(optval, optlen);
+ if (IS_ERR(new_opt)) {
+ retv = PTR_ERR(new_opt);
+ break;
+ }
+
+ opt = rcu_dereference_protected(np->opt,
+ lockdep_sock_is_held(sk));
+
+ switch (optname) {
+ case IPV6_HOPOPTS_DEL_TLV:
+ deleting = true;
+ /* Fallthrough */
+ case IPV6_HOPOPTS_TLV:
+ if (opt)
+ old = opt->hopopt;
+ which = IPV6_HOPOPTS;
+ break;
+ case IPV6_RTHDRDSTOPTS_DEL_TLV:
+ deleting = true;
+ /* Fallthrough */
+ case IPV6_RTHDRDSTOPTS_TLV:
+ if (opt)
+ old = opt->dst0opt;
+ which = IPV6_RTHDRDSTOPTS;
+ break;
+ case IPV6_DSTOPTS_DEL_TLV:
+ deleting = true;
+ /* Fallthrough */
+ case IPV6_DSTOPTS_TLV:
+ if (opt)
+ old = opt->dst1opt;
+ which = IPV6_DSTOPTS;
+ break;
+ }
+
+ admin = ns_capable(net->user_ns, CAP_NET_RAW);
+
+ retv = ipv6_opt_validate_single_tlv(net, which, new_opt, optlen,
+ deleting, admin);
+ if (retv < 0)
+ break;
+
+ if (deleting) {
+ if (!old)
+ break;
+ new = ipv6_opt_tlv_delete(net, old, new_opt, admin);
+ } else {
+ new = ipv6_opt_tlv_insert(net, old, which, new_opt,
+ admin);
+ }
+
+ kfree(new_opt);
+
+ if (IS_ERR(new)) {
+ retv = PTR_ERR(new);
+ break;
+ }
+
+ retv = ipv6_opt_update(sk, opt, which, new);
+
+ kfree(new);
+
+ break;
+ }
+
case IPV6_PKTINFO:
{
struct in6_pktinfo pkt;
--
2.7.4
^ permalink raw reply related
* [PATCH v2 net-next 4/5] ip6tlvs: Validation of TX Destination and Hop-by-Hop options
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net>
Validate Destination and Hop-by-Hop options. This uses the information
in the TLV parameters table to validate various aspects of both
individual TLVs as well as a list of TLVs in an extension header.
There are two levels of validation that can be performed: simple checks
and deep checks. Simple checks validate on the most basic properties
such as that the TLV list fits into the EH. Deep checks do a fine
grained validation that includes perferred ordering, length limits,
and length alignment..
With proper permissions set in the TLV parameter table, this patch
allows non-privileged users to send TLVs. Given that TLVs are open
ended and potentially a source of DOS attack, deep checks are
performed to limit the format that a non-privileged user can send.
If deep checks are enabled, a canonical format for sending TLVs is
enforced (in adherence with the robustness principle). A TLV must
be well ordered with respect to the preferred order for the TLV.
Each TLV must be aligned as described in the parameter table. Minimal
padding (one padding TLV) is used to align TLVs. The length of the
extension header as well as the count of non-padding TLVs is checked
against max_*_opts_len and max_*_opts_cnt. For individual TLVs, lengths
length alignment is checked.
---
include/net/ipv6.h | 7 ++
net/ipv6/datagram.c | 27 ++--
net/ipv6/exthdrs_options.c | 298 +++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ipv6_sockglue.c | 30 ++++-
4 files changed, 348 insertions(+), 14 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 9992f17..c5692d6 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,6 +379,13 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
+int ipv6_opt_validate_tlvs(struct net *net, struct ipv6_opt_hdr *opt,
+ unsigned int optname, bool admin);
+int ipv6_opt_validate_single_tlv(struct net *net, unsigned int optname,
+ unsigned char *tlv, size_t len,
+ bool deleting, bool admin);
+int ipv6_opt_check_perm(struct net *net, struct sock *sk,
+ int optname, bool admin);
struct tlv_tx_param {
unsigned char preferred_order;
unsigned char admin_perm : 2;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index ee4a4e5..ef50439 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -853,10 +853,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
- err = -EPERM;
+
+ err = ipv6_opt_validate_tlvs(net, hdr, IPV6_HOPOPTS,
+ ns_capable(net->user_ns,
+ CAP_NET_RAW));
+ if (err < 0)
goto exit_f;
- }
+
opt->opt_nflen += len;
opt->hopopt = hdr;
break;
@@ -873,10 +876,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
- err = -EPERM;
+
+ err = ipv6_opt_validate_tlvs(net, hdr, IPV6_DSTOPTS,
+ ns_capable(net->user_ns,
+ CAP_NET_RAW));
+ if (err < 0)
goto exit_f;
- }
+
if (opt->dst1opt) {
err = -EINVAL;
goto exit_f;
@@ -898,10 +904,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
- err = -EPERM;
+
+ err = ipv6_opt_validate_tlvs(net, hdr, cmsg->cmsg_type,
+ ns_capable(net->user_ns,
+ CAP_NET_RAW));
+ if (err < 0)
goto exit_f;
- }
+
if (cmsg->cmsg_type == IPV6_DSTOPTS) {
opt->opt_flen += len;
opt->dst1opt = hdr;
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index a7811e0..f334af5 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -162,6 +162,304 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
}
EXPORT_SYMBOL_GPL(ipv6_fixup_options);
+/* TLV validation functions */
+
+/* Validate a single non-padding TLV */
+static int __ipv6_opt_validate_single_tlv(struct net *net, unsigned char *tlv,
+ struct tlv_tx_param *tptx,
+ unsigned int class, bool *deep_check,
+ bool deleting, bool admin)
+{
+ if (tlv[0] < 2) /* Must be non-padding */
+ return -EINVAL;
+
+ /* Check permissions */
+ switch (admin ? tptx->admin_perm : tptx->user_perm) {
+ case IPV6_TLV_PERM_NO_CHECK:
+ /* Allowed with no deep checks */
+ *deep_check = false;
+ return 0;
+ case IPV6_TLV_PERM_WITH_CHECK:
+ /* Allowed with deep checks */
+ *deep_check = true;
+ break;
+ default:
+ /* No permission */
+ return -EPERM;
+ }
+
+ /* Perform deep checks on the TLV */
+
+ /* Check class */
+ if ((tptx->class & class) != class)
+ return -EINVAL;
+
+ /* Don't bother checking lengths when deleting, the TLV is only
+ * needed here for lookup
+ */
+ if (deleting) {
+ /* Don't bother with deep checks when deleting */
+ *deep_check = false;
+ } else {
+ /* Check length */
+ if (tlv[1] < tptx->min_data_len || tlv[1] > tptx->max_data_len)
+ return -EINVAL;
+
+ /* Check length alignment */
+ if ((tlv[1] % (tptx->data_len_mult + 1)) != tptx->data_len_off)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int optname_to_tlv_class(int optname)
+{
+ switch (optname) {
+ case IPV6_HOPOPTS:
+ return IPV6_TLV_CLASS_FLAG_HOPOPT;
+ case IPV6_RTHDRDSTOPTS:
+ return IPV6_TLV_CLASS_FLAG_RTRDSTOPT;
+ case IPV6_DSTOPTS:
+ return IPV6_TLV_CLASS_FLAG_DSTOPT;
+ default:
+ return -1U;
+ }
+}
+
+static int __ipv6_opt_validate_tlvs(struct net *net, struct ipv6_opt_hdr *opt,
+ unsigned int optname, bool deleting,
+ bool admin)
+{
+ unsigned int max_len = 0, max_cnt = 0, cnt = 0;
+ unsigned char *tlv = (unsigned char *)opt;
+ bool deep_check, did_deep_check = false;
+ unsigned int opt_len, tlv_len, offset;
+ unsigned int padding = 0, numpad = 0;
+ unsigned char prev_tlv_order = 0;
+ struct tlv_tx_param *tptx;
+ int retc, ret = -EINVAL;
+ unsigned int class;
+
+ opt_len = ipv6_optlen(opt);
+ offset = sizeof(*opt);
+
+ class = optname_to_tlv_class(optname);
+
+ switch (optname) {
+ case IPV6_HOPOPTS:
+ max_len = net->ipv6.sysctl.max_hbh_opts_len;
+ max_cnt = net->ipv6.sysctl.max_hbh_opts_cnt;
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ case IPV6_DSTOPTS:
+ max_len = net->ipv6.sysctl.max_dst_opts_len;
+ max_cnt = net->ipv6.sysctl.max_dst_opts_cnt;
+ break;
+ }
+
+ rcu_read_lock();
+
+ while (offset < opt_len) {
+ switch (tlv[offset]) {
+ case IPV6_TLV_PAD1:
+ tlv_len = 1;
+ padding++;
+ numpad++;
+ break;
+ case IPV6_TLV_PADN:
+ if (offset + 1 >= opt_len)
+ goto out;
+
+ tlv_len = tlv[offset + 1] + 2;
+
+ if (offset + tlv_len > opt_len)
+ goto out;
+
+ padding += tlv_len;
+ numpad++;
+ break;
+ default:
+ if (offset + 1 >= opt_len)
+ goto out;
+
+ tlv_len = tlv[offset + 1] + 2;
+
+ if (offset + tlv_len > opt_len)
+ goto out;
+
+ tptx = tlv_deref_tx_params(tlv[offset]);
+ retc = __ipv6_opt_validate_single_tlv(net, &tlv[offset],
+ tptx, class,
+ &deep_check,
+ deleting, admin);
+ if (retc < 0) {
+ ret = retc;
+ goto out;
+ }
+
+ if (deep_check) {
+ /* Check for too many options */
+ if (++cnt > max_cnt) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ /* Check order */
+ if (tptx->preferred_order < prev_tlv_order)
+ goto out;
+
+ /* Check alignment */
+ if ((offset % (tptx->align_mult + 1)) !=
+ tptx->align_off)
+ goto out;
+
+ /* Check for right amount of padding */
+ if (numpad > 1 || padding > tptx->align_mult)
+ goto out;
+
+ prev_tlv_order = tptx->preferred_order;
+ }
+
+ padding = 0;
+ numpad = 0;
+ did_deep_check = true;
+ }
+ offset += tlv_len;
+ }
+
+ /* If we did at least one deep check apply length limit */
+ if (did_deep_check && opt_len > max_len) {
+ ret = -EMSGSIZE;
+ goto out;
+ }
+
+ /* All good */
+ ret = 0;
+out:
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/**
+ * __ipv6_opt_validate_tlvs - Validate TLVs.
+ * @net: Current net
+ * @opt: The option header
+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS
+ * @admin: Set for privileged user
+ *
+ * Description:
+ * Walks the TLVs in a list to verify that the TLV lengths and other
+ * parameters are in bounds for a Destination or Hop-by-Hop option.
+ * Return -EINVAL is there is a problem, zero otherwise.
+ */
+int ipv6_opt_validate_tlvs(struct net *net, struct ipv6_opt_hdr *opt,
+ unsigned int optname, bool admin)
+{
+ return __ipv6_opt_validate_tlvs(net, opt, optname, false, admin);
+}
+EXPORT_SYMBOL(ipv6_opt_validate_tlvs);
+
+/**
+ * ipv6_opt_validate_single - Check that a single TLV is valid.
+ * @net: Current net
+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS
+ * @tlv: The TLV as array of bytes
+ * @len: Length of buffer holding TLV
+ *
+ * Description:
+ * Validates a single TLV. The TLV must be non-padding type. The length
+ * of the TLV (as determined by the second byte that gives length of the
+ * option data) must match @len.
+ */
+int ipv6_opt_validate_single_tlv(struct net *net, unsigned int optname,
+ unsigned char *tlv, size_t len,
+ bool deleting, bool admin)
+{
+ struct tlv_tx_param *tptx;
+ unsigned int class;
+ bool deep_check;
+ int ret = 0;
+
+ class = optname_to_tlv_class(optname);
+
+ switch (tlv[0]) {
+ case IPV6_TLV_PAD1:
+ case IPV6_TLV_PADN:
+ return -EINVAL;
+ default:
+ break;
+ }
+
+ if (len < 2)
+ return -EINVAL;
+
+ if (tlv[1] + 2 != len)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ tptx = tlv_deref_tx_params(tlv[0]);
+
+ ret = __ipv6_opt_validate_single_tlv(net, tlv, tptx, class,
+ &deep_check, deleting, admin);
+
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ipv6_opt_validate_single_tlv);
+
+/**
+ * ipv6_opt_check_perm - Check is current capabilities allows modify
+ * txopts.
+ * @net: Current net
+ * @sk: the socket
+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS
+ * @admin: Set for privileged user
+ *
+ * Description:
+ *
+ * Checks whether the permissions of TLV that are set on a socket permit
+ * modificationr.
+ *
+ */
+int ipv6_opt_check_perm(struct net *net, struct sock *sk, int optname,
+ bool admin)
+{
+ struct ipv6_txoptions *old = txopt_get(inet6_sk(sk));
+ struct ipv6_opt_hdr *opt;
+ int retv = -EPERM;
+
+ if (!old)
+ return 0;
+
+ switch (optname) {
+ case IPV6_HOPOPTS:
+ opt = old->hopopt;
+ break;
+ case IPV6_RTHDRDSTOPTS:
+ opt = old->dst0opt;
+ break;
+ case IPV6_DSTOPTS:
+ opt = old->dst1opt;
+ break;
+ default:
+ goto out;
+ }
+
+ /* Just call the validate function on the options as being
+ * deleted.
+ */
+ retv = __ipv6_opt_validate_tlvs(net, opt, optname, true, admin);
+
+out:
+ txopt_put(old);
+ return retv;
+}
+EXPORT_SYMBOL(ipv6_opt_check_perm);
+
/* Destination options header */
#if IS_ENABLED(CONFIG_IPV6_MIP6)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 973e215..009c8a4 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -400,11 +400,6 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
struct ipv6_txoptions *opt;
struct ipv6_opt_hdr *new = NULL;
- /* hop-by-hop / destination options are privileged option */
- retv = -EPERM;
- if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
- break;
-
/* remove any sticky options header with a zero option
* length, per RFC3542.
*/
@@ -427,6 +422,31 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
}
}
+ if (optname != IPV6_RTHDR) {
+ bool cap = ns_capable(net->user_ns, CAP_NET_RAW);
+
+ /* First check if we have permission to delete
+ * the existing options on the socket.
+ */
+ retv = ipv6_opt_check_perm(net, sk, optname, cap);
+ if (retv < 0) {
+ kfree(new);
+ break;
+ }
+
+ /* Check permissions and other validations on new
+ * TLVs
+ */
+ if (new) {
+ retv = ipv6_opt_validate_tlvs(net, new,
+ optname, cap);
+ if (retv < 0) {
+ kfree(new);
+ break;
+ }
+ }
+ }
+
opt = rcu_dereference_protected(np->opt,
lockdep_sock_is_held(sk));
opt = ipv6_renew_options(sk, opt, optname, new);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 net-next 3/5] ip6tlvs: Add netlink interface
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net>
Add a netlink interface to manage the TX TLV parameters. Managed
parameters include those for validating and sending TLVs being sent
such as alignment, TLV ordering, length limits, etc.
---
include/uapi/linux/in6.h | 32 +++++
net/ipv6/exthdrs_options.c | 300 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 331 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 38e8e63..a54cf96 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -297,6 +297,38 @@ struct in6_flowlabel_req {
* MRT6_MAX
*/
+/* NETLINK_GENERIC related info for IPv6 TLVs */
+
+#define IPV6_TLV_GENL_NAME "ipv6-tlv"
+#define IPV6_TLV_GENL_VERSION 0x1
+
+enum {
+ IPV6_TLV_ATTR_UNSPEC,
+ IPV6_TLV_ATTR_TYPE, /* u8, > 1 */
+ IPV6_TLV_ATTR_ORDER, /* u8 */
+ IPV6_TLV_ATTR_ADMIN_PERM, /* u8, perm value */
+ IPV6_TLV_ATTR_USER_PERM, /* u8, perm value */
+ IPV6_TLV_ATTR_CLASS, /* u8, 3 bit flags */
+ IPV6_TLV_ATTR_ALIGN_MULT, /* u8, 1 to 16 */
+ IPV6_TLV_ATTR_ALIGN_OFF, /* u8, 0 to 15 */
+ IPV6_TLV_ATTR_MIN_DATA_LEN, /* u8 (option data length) */
+ IPV6_TLV_ATTR_MAX_DATA_LEN, /* u8 (option data length) */
+ IPV6_TLV_ATTR_DATA_LEN_MULT, /* u8, 1 to 16 */
+ IPV6_TLV_ATTR_DATA_LEN_OFF, /* u8, 0 to 15 */
+
+ __IPV6_TLV_ATTR_MAX,
+};
+
+#define IPV6_TLV_ATTR_MAX (__IPV6_TLV_ATTR_MAX - 1)
+
+enum {
+ IPV6_TLV_CMD_SET,
+ IPV6_TLV_CMD_UNSET,
+ IPV6_TLV_CMD_GET,
+
+ __IPV6_TLV_CMD_MAX,
+};
+
/* TLV permissions values */
enum {
IPV6_TLV_PERM_NONE,
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 397d9b3..a7811e0 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -6,11 +6,13 @@
#include <linux/socket.h>
#include <linux/types.h>
#include <net/calipso.h>
+#include <net/genetlink.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
+#include <uapi/linux/genetlink.h>
#include <uapi/linux/in.h>
/* Parsing tlv encoded headers.
@@ -629,12 +631,298 @@ static const struct tlv_init_params tlv_init_params[] __initconst = {
}
};
+static struct genl_family tlv_nl_family;
+
+static const struct nla_policy tlv_nl_policy[IPV6_TLV_ATTR_MAX + 1] = {
+ [IPV6_TLV_ATTR_TYPE] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_ORDER] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_ADMIN_PERM] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_USER_PERM] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_CLASS] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_ALIGN_MULT] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_ALIGN_OFF] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_MIN_DATA_LEN] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_MAX_DATA_LEN] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_DATA_LEN_OFF] = { .type = NLA_U8, },
+ [IPV6_TLV_ATTR_DATA_LEN_MULT] = { .type = NLA_U8, },
+};
+
+static int tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info)
+{
+ struct tlv_tx_param new_tx, *tptx;
+ int retv = -EINVAL, i;
+ u8 tlv_type, v;
+
+ if (!info->attrs[IPV6_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]);
+ if (tlv_type < 2)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ new_tx = *tlv_deref_tx_params(tlv_type);
+
+ if (info->attrs[IPV6_TLV_ATTR_ORDER]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ORDER]);
+ if (v) {
+ struct tlv_tx_param *tptx;
+
+ for (i = 2; i < 256; i++) {
+ /* Preferred orders must be unique */
+ tptx = tlv_deref_tx_params(i);
+ if (tptx->preferred_order == v &&
+ i != tlv_type) {
+ retv = -EALREADY;
+ goto out;
+ }
+ }
+ new_tx.preferred_order = v;
+ }
+ }
+
+ if (!new_tx.preferred_order) {
+ unsigned long check_map[BITS_TO_LONGS(255)];
+ int pos;
+
+ /* Preferred order not specified, automatically set one.
+ * This is chosen to be the first value after the greatest
+ * order in use.
+ */
+ memset(check_map, 0, sizeof(check_map));
+
+ for (i = 2; i < 256; i++) {
+ unsigned int order;
+
+ tptx = tlv_deref_tx_params(i);
+ order = tptx->preferred_order;
+
+ if (!order)
+ continue;
+
+ WARN_ON(test_bit(255 - order, check_map));
+ set_bit(255 - order, check_map);
+ }
+
+ pos = find_first_bit(check_map, 255);
+ if (pos)
+ new_tx.preferred_order = 255 - (pos - 1);
+ else
+ new_tx.preferred_order = 255 -
+ find_first_zero_bit(check_map, sizeof(check_map));
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]);
+ if (v > IPV6_TLV_PERM_MAX)
+ goto out;
+ new_tx.admin_perm = v;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_USER_PERM]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_USER_PERM]);
+ if (v > IPV6_TLV_PERM_MAX)
+ goto out;
+ new_tx.user_perm = v;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_CLASS]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_CLASS]);
+ if (v > IPV6_TLV_CLASS_MAX)
+ goto out;
+ new_tx.class = v;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]);
+ if (v > 16 || v < 1)
+ goto out;
+ new_tx.align_mult = v - 1;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]);
+ if (v > 15)
+ goto out;
+ new_tx.align_off = v;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN])
+ new_tx.max_data_len =
+ nla_get_u8(info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]);
+
+ if (info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN])
+ new_tx.min_data_len =
+ nla_get_u8(info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN]);
+
+ if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]);
+ if (v > 16 || v < 1)
+ goto out;
+ new_tx.data_len_mult = v - 1;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]);
+ if (v > 15)
+ goto out;
+ new_tx.data_len_off = v;
+ }
+
+ retv = tlv_set_tx_param(tlv_type, &new_tx);
+
+out:
+ rcu_read_unlock();
+ return retv;
+}
+
+static int tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info)
+{
+ unsigned int tlv_type;
+
+ if (!info->attrs[IPV6_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]);
+ if (tlv_type < 2)
+ return -EINVAL;
+
+ return tlv_unset_tx_param(tlv_type);
+}
+
+static int tlv_fill_info(int tlv_type, struct sk_buff *msg, bool admin)
+{
+ struct tlv_tx_param *tptx;
+ int ret = 0;
+
+ rcu_read_lock();
+
+ tptx = tlv_deref_tx_params(tlv_type);
+
+ if (nla_put_u8(msg, IPV6_TLV_ATTR_TYPE, tlv_type) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ORDER, tptx->preferred_order) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_USER_PERM, tptx->user_perm) ||
+ (admin && nla_put_u8(msg, IPV6_TLV_ATTR_ADMIN_PERM,
+ tptx->admin_perm)) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_CLASS, tptx->class) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_MULT, tptx->align_mult + 1) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_OFF, tptx->align_off) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_MIN_DATA_LEN, tptx->min_data_len) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_MAX_DATA_LEN, tptx->max_data_len) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_MULT,
+ tptx->data_len_mult + 1) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_OFF, tptx->data_len_off))
+ ret = -1;
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static int tlv_dump_info(int tlv_type, u32 portid, u32 seq, u32 flags,
+ struct sk_buff *skb, u8 cmd, bool admin)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, portid, seq, &tlv_nl_family, flags, cmd);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (tlv_fill_info(tlv_type, skb, admin) < 0) {
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+ }
+
+ genlmsg_end(skb, hdr);
+
+ return 0;
+}
+
+static int tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ int ret, tlv_type;
+
+ if (!info->attrs[IPV6_TLV_ATTR_TYPE])
+ return -EINVAL;
+
+ tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]);
+ if (tlv_type < 2)
+ return -EINVAL;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = tlv_dump_info(tlv_type, info->snd_portid, info->snd_seq, 0, msg,
+ info->genlhdr->cmd,
+ netlink_capable(skb, CAP_NET_ADMIN));
+ if (ret < 0) {
+ nlmsg_free(msg);
+ return ret;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0, ret, i;
+
+ for (i = 2; i < 256; i++) {
+ if (idx++ < cb->args[0])
+ continue;
+ ret = tlv_dump_info(i, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ skb, IPV6_TLV_CMD_GET,
+ netlink_capable(cb->skb, CAP_NET_ADMIN));
+ if (ret)
+ break;
+ }
+
+ cb->args[0] = idx;
+ return skb->len;
+}
+
+static const struct genl_ops tlv_nl_ops[] = {
+{
+ .cmd = IPV6_TLV_CMD_SET,
+ .doit = tlv_nl_cmd_set,
+ .policy = tlv_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPV6_TLV_CMD_UNSET,
+ .doit = tlv_nl_cmd_unset,
+ .policy = tlv_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPV6_TLV_CMD_GET,
+ .doit = tlv_nl_cmd_get,
+ .dumpit = tlv_nl_dump,
+ .policy = tlv_nl_policy,
+},
+};
+
+static struct genl_family tlv_nl_family __ro_after_init = {
+ .hdrsize = 0,
+ .name = IPV6_TLV_GENL_NAME,
+ .version = IPV6_TLV_GENL_VERSION,
+ .maxattr = IPV6_TLV_ATTR_MAX,
+ .netnsok = true,
+ .module = THIS_MODULE,
+ .ops = tlv_nl_ops,
+ .n_ops = ARRAY_SIZE(tlv_nl_ops),
+};
+
static int __init exthdrs_init(void)
{
unsigned long check_map[BITS_TO_LONGS(256)];
struct tlv_param_table *tpt;
size_t tsize;
- int i;
+ int i, ret;
memset(check_map, 0, sizeof(check_map));
@@ -676,11 +964,21 @@ static int __init exthdrs_init(void)
RCU_INIT_POINTER(tlv_param_table, tpt);
+ ret = genl_register_family(&tlv_nl_family);
+ if (ret < 0)
+ goto fail_genl_register;
+
return 0;
+
+fail_genl_register:
+ __tlv_destroy_param_table();
+
+ return ret;
}
module_init(exthdrs_init);
static void __exit exthdrs_fini(void)
{
+ genl_unregister_family(&tlv_nl_family);
}
module_exit(exthdrs_fini);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 net-next 2/5] exthdrs: Registration of TLV handlers and parameters
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net>
Create a single TLV parameter table that holds meta information for IPv6
Hop-by-Hop and Destination TLVs. The data structure is composed of a 256
element array of u8's (one entry for each TLV type to allow O(1)
lookup). Each entry provides an offset into an array of TLV parameter
data structures which follows the array of u8s. TLV. The TLV parameter
data structure contains parameters and handler functions for receiving
and transmitting TLVs. The receive and transmit properties can be
managed independently. The zeroth element in the TLV parameter array
provides default parameters for TLVs.
TLV transmit properties include a description of limits, alignment, and
preferred ordering. TLV receive properties provide the receiver handler.
A class attribute is defined in both receive and transmit properties
that indicates the type of extension header in which the TLV may be used
(e.g. Hop-by-Hop options, Destination options, or Destination options
before the routing header).
Receive TLV lookup and processing is modified to be a lookup in the TLV
parameter table. tlv_{set,unset}_{rx,tx}_param function can be used to
set attributes in the TLV table. A table containing parameters for TLVs
supported by the kernel and is used to initialize the TLV table.
---
include/net/ipv6.h | 64 +++++++-
include/uapi/linux/in6.h | 17 ++
net/ipv6/exthdrs.c | 47 +++---
net/ipv6/exthdrs_options.c | 384 ++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 465 insertions(+), 47 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 8abdcdb..9992f17 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,13 +379,67 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
-struct tlvtype_proc {
- int type;
- bool (*func)(struct sk_buff *skb, int offset);
+struct tlv_tx_param {
+ unsigned char preferred_order;
+ unsigned char admin_perm : 2;
+ unsigned char user_perm : 2;
+ unsigned char class : 3;
+ unsigned char align_mult : 4;
+ unsigned char align_off : 4;
+ unsigned char data_len_mult : 4;
+ unsigned char data_len_off : 4;
+ unsigned char min_data_len;
+ unsigned char max_data_len;
};
-extern const struct tlvtype_proc tlvprocdestopt_lst[];
-extern const struct tlvtype_proc tlvprochopopt_lst[];
+struct tlv_rx_param {
+ unsigned char class: 3;
+ bool (*func)(unsigned int class, struct sk_buff *skb, int offset);
+};
+
+struct tlv_param {
+ struct tlv_tx_param tx_params;
+ struct tlv_rx_param rx_params;
+};
+
+struct tlv_param_table {
+ unsigned char entries[256];
+ unsigned char count;
+ struct rcu_head rcu;
+ struct tlv_param params[0];
+};
+
+extern struct tlv_param_table __rcu *tlv_param_table;
+
+/* Preferred TLV ordering (placed by increasing order) */
+#define TLV_PREF_ORDER_HAO 10
+#define TLV_PREF_ORDER_ROUTERALERT 20
+#define TLV_PREF_ORDER_JUMBO 30
+#define TLV_PREF_ORDER_CALIPSO 40
+
+/* tlv_deref_rx_params assume rcu_read_lock is held */
+static inline struct tlv_rx_param *tlv_deref_rx_params(unsigned int type)
+{
+ struct tlv_param_table *tpt = rcu_dereference(tlv_param_table);
+
+ return &tpt->params[tpt->entries[type]].rx_params;
+}
+
+/* tlv_deref_tx_params assume rcu_read_lock is held */
+static inline struct tlv_tx_param *tlv_deref_tx_params(unsigned int type)
+{
+ struct tlv_param_table *tpt = rcu_dereference(tlv_param_table);
+
+ return &tpt->params[tpt->entries[type]].tx_params;
+}
+
+int tlv_set_param(unsigned char type,
+ const struct tlv_rx_param *rx_param_tmpl,
+ const struct tlv_tx_param *tx_param_tmpl);
+int tlv_unset_rx_param(unsigned char type);
+int tlv_set_rx_param(unsigned char type, struct tlv_rx_param *rx_param_tmpl);
+int tlv_unset_tx_param(unsigned char type);
+int tlv_set_tx_param(unsigned char type, struct tlv_tx_param *tx_param_tmpl);
bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
const struct inet6_skb_parm *opt);
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 71d82fe..38e8e63 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -296,4 +296,21 @@ struct in6_flowlabel_req {
* ...
* MRT6_MAX
*/
+
+/* TLV permissions values */
+enum {
+ IPV6_TLV_PERM_NONE,
+ IPV6_TLV_PERM_WITH_CHECK,
+ IPV6_TLV_PERM_NO_CHECK,
+ IPV6_TLV_PERM_MAX = IPV6_TLV_PERM_NO_CHECK
+};
+
+/* Flags for EH type that can use a TLV option */
+#define IPV6_TLV_CLASS_FLAG_HOPOPT 0x1
+#define IPV6_TLV_CLASS_FLAG_RTRDSTOPT 0x2
+#define IPV6_TLV_CLASS_FLAG_DSTOPT 0x4
+#define IPV6_TLV_CLASS_MAX 0x7
+
+#define IPV6_TLV_CLASS_ANY_DSTOPT (IPV6_TLV_CLASS_FLAG_RTRDSTOPT | \
+ IPV6_TLV_CLASS_FLAG_DSTOPT)
#endif /* _UAPI_LINUX_IN6_H */
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6dbacf1..af4152e 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -100,15 +100,14 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
/* Parse tlv encoded option header (hop-by-hop or destination) */
-static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
- struct sk_buff *skb,
+static bool ip6_parse_tlv(unsigned int class, struct sk_buff *skb,
int max_count)
{
int len = (skb_transport_header(skb)[1] + 1) << 3;
const unsigned char *nh = skb_network_header(skb);
int off = skb_network_header_len(skb);
- const struct tlvtype_proc *curr;
bool disallow_unknowns = false;
+ struct tlv_rx_param *tprx;
int tlv_count = 0;
int padlen = 0;
@@ -117,12 +116,16 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
max_count = -max_count;
}
- if (skb_transport_offset(skb) + len > skb_headlen(skb))
- goto bad;
+ if (skb_transport_offset(skb) + len > skb_headlen(skb)) {
+ kfree_skb(skb);
+ return false;
+ }
off += 2;
len -= 2;
+ rcu_read_unlock();
+
while (len > 0) {
int optlen = nh[off + 1] + 2;
int i;
@@ -162,19 +165,19 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
if (tlv_count > max_count)
goto bad;
- for (curr = procs; curr->type >= 0; curr++) {
- if (curr->type == nh[off]) {
- /* type specific length/alignment
- checks will be performed in the
- func(). */
- if (curr->func(skb, off) == false)
- return false;
- break;
- }
+ tprx = tlv_deref_rx_params(nh[off]);
+
+ if ((tprx->class & class) && tprx->func) {
+ /* Handler will apply additional checks to
+ * the TLV
+ */
+ if (!tprx->func(class, skb, off))
+ goto bad_nofree;
+ } else if (!ip6_tlvopt_unknown(skb, off,
+ disallow_unknowns)) {
+ /* No appropriate handler, TLV is unknown */
+ goto bad_nofree;
}
- if (curr->type < 0 &&
- !ip6_tlvopt_unknown(skb, off, disallow_unknowns))
- return false;
padlen = 0;
break;
@@ -183,10 +186,14 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
len -= optlen;
}
- if (len == 0)
+ if (len == 0) {
+ rcu_read_unlock();
return true;
+ }
bad:
kfree_skb(skb);
+bad_nofree:
+ rcu_read_unlock();
return false;
}
@@ -220,7 +227,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
dstbuf = opt->dst1;
#endif
- if (ip6_parse_tlv(tlvprocdestopt_lst, skb,
+ if (ip6_parse_tlv(IPV6_TLV_CLASS_FLAG_DSTOPT, skb,
init_net.ipv6.sysctl.max_dst_opts_cnt)) {
skb->transport_header += extlen;
opt = IP6CB(skb);
@@ -643,7 +650,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
goto fail_and_free;
opt->flags |= IP6SKB_HOPBYHOP;
- if (ip6_parse_tlv(tlvprochopopt_lst, skb,
+ if (ip6_parse_tlv(IPV6_TLV_CLASS_FLAG_HOPOPT, skb,
init_net.ipv6.sysctl.max_hbh_opts_cnt)) {
skb->transport_header += extlen;
opt = IP6CB(skb);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 70266a6..397d9b3 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -11,6 +11,7 @@
#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
+#include <uapi/linux/in.h>
/* Parsing tlv encoded headers.
*
@@ -19,6 +20,8 @@
* It MUST NOT touch skb->h.
*/
+struct tlv_param_table __rcu *tlv_param_table;
+
struct ipv6_txoptions *
ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
{
@@ -160,7 +163,7 @@ EXPORT_SYMBOL_GPL(ipv6_fixup_options);
/* Destination options header */
#if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+static bool ipv6_dest_hao(unsigned int class, struct sk_buff *skb, int optoff)
{
struct ipv6_destopt_hao *hao;
struct inet6_skb_parm *opt = IP6CB(skb);
@@ -219,16 +222,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
}
#endif
-const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
- {
- .type = IPV6_TLV_HAO,
- .func = ipv6_dest_hao,
- },
-#endif
- {-1, NULL}
-};
-
/* Hop-by-hop options */
/* Note: we cannot rely on skb_dst(skb) before we assign it in
@@ -247,7 +240,7 @@ static inline struct net *ipv6_skb_net(struct sk_buff *skb)
/* Router Alert as of RFC 2711 */
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_ra(unsigned int class, struct sk_buff *skb, int optoff)
{
const unsigned char *nh = skb_network_header(skb);
@@ -265,7 +258,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
/* Jumbo payload */
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_jumbo(unsigned int class, struct sk_buff *skb, int optoff)
{
const unsigned char *nh = skb_network_header(skb);
struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
@@ -309,7 +302,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
/* CALIPSO RFC 5570 */
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_calipso(unsigned int class, struct sk_buff *skb,
+ int optoff)
{
const unsigned char *nh = skb_network_header(skb);
@@ -329,18 +323,364 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
return false;
}
-const struct tlvtype_proc tlvprochopopt_lst[] = {
+/* TLV parameter table functions and structures */
+
+static void tlv_param_table_release(struct rcu_head *rcu)
+{
+ struct tlv_param_table *tpt =
+ container_of(rcu, struct tlv_param_table, rcu);
+
+ vfree(tpt);
+}
+
+/* Default (unset) values for TX TLV parameters */
+static const struct tlv_param tlv_default_param = {
+ .tx_params.preferred_order = 0,
+ .tx_params.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .tx_params.user_perm = IPV6_TLV_PERM_NONE,
+ .tx_params.class = 0,
+ .tx_params.align_mult = (4 - 1), /* Default alignment: 4n + 2 */
+ .tx_params.align_off = 2,
+ .tx_params.min_data_len = 0,
+ .tx_params.max_data_len = 255,
+ .tx_params.data_len_mult = (1 - 1), /* No default length align */
+ .tx_params.data_len_off = 0,
+};
+
+static DEFINE_MUTEX(tlv_mutex);
+
+static size_t tlv_param_table_size(unsigned char count)
+{
+ return sizeof(struct tlv_param_table) +
+ (count * sizeof(struct tlv_param));
+}
+
+/* mutex held */
+static int __tlv_set_param(unsigned char type,
+ const struct tlv_rx_param *rx_param_tmpl,
+ const struct tlv_tx_param *tx_param_tmpl)
+{
+ struct tlv_param_table *tpt, *old;
+ unsigned char count, pos;
+ struct tlv_param *tp;
+
+ old = rcu_dereference_protected(tlv_param_table,
+ lockdep_is_held(&tlv_mutex));
+
+ if (old->entries[type]) {
+ pos = old->entries[type];
+ count = old->count;
+ } else {
+ pos = old->count;
+ count = pos + 1;
+ }
+
+ tpt = vmalloc(tlv_param_table_size(count));
+ if (!tpt)
+ return -ENOMEM;
+
+ memcpy(tpt, old, tlv_param_table_size(old->count));
+
+ tp = &tpt->params[pos];
+ memcpy(&tp->rx_params, rx_param_tmpl, sizeof(tp->rx_params));
+ memcpy(&tp->tx_params, tx_param_tmpl, sizeof(tp->tx_params));
+
+ tpt->entries[type] = pos;
+ tpt->count = count;
+
+ rcu_assign_pointer(tlv_param_table, tpt);
+
+ call_rcu(&old->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+/* mutex held */
+static int __tlv_unset_param(unsigned char type)
+{
+ struct tlv_param_table *tpt, *old;
+ struct tlv_param *tp;
+ unsigned char pos;
+ int i;
+
+ old = rcu_dereference_protected(tlv_param_table,
+ lockdep_is_held(&tlv_mutex));
+
+ if (!old->entries[type])
+ return 0;
+
+ tpt = vmalloc(tlv_param_table_size(old->count - 1));
+ if (!tpt)
+ return -ENOMEM;
+
+ pos = old->entries[type];
+
+ memcpy(tpt->params, old->params, pos * sizeof(*tp));
+ memcpy(&tpt->params[pos], &old->params[pos + 1],
+ (old->count - pos - 1) * sizeof(*tp));
+
+ for (i = 0; i < 256; i++) {
+ if (old->entries[i] > pos)
+ tpt->entries[i] = old->entries[i] - 1;
+ else
+ tpt->entries[i] = old->entries[i];
+ }
+
+ tpt->entries[type] = 0;
+
+ tpt->count = old->count - 1;
+
+ rcu_assign_pointer(tlv_param_table, tpt);
+
+ call_rcu(&old->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+void __tlv_destroy_param_table(void)
+{
+ struct tlv_param_table *tpt;
+
+ mutex_lock(&tlv_mutex);
+
+ tpt = rcu_dereference_protected(tlv_param_table,
+ lockdep_is_held(&tlv_mutex));
+ if (tpt) {
+ rcu_assign_pointer(tlv_param_table, tpt);
+ call_rcu(&tpt->rcu, tlv_param_table_release);
+ }
+
+ mutex_unlock(&tlv_mutex);
+}
+
+int tlv_set_param(unsigned char type,
+ const struct tlv_rx_param *rx_param_tmpl,
+ const struct tlv_tx_param *tx_param_tmpl)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ ret = __tlv_set_param(type, rx_param_tmpl, tx_param_tmpl);
+
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_set_param);
+
+int tlv_unset_rx_param(unsigned char type)
+{
+ struct tlv_tx_param *tptx;
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ tptx = tlv_deref_tx_params(type);
+
+ if (!tptx->preferred_order)
+ ret = __tlv_unset_param(type);
+ else
+ ret = __tlv_set_param(type, &tlv_default_param.rx_params, tptx);
+
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_unset_rx_param);
+
+int tlv_set_rx_param(unsigned char type, struct tlv_rx_param *rx_param_tmpl)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ ret = __tlv_set_param(type, rx_param_tmpl, tlv_deref_tx_params(type));
+
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_set_rx_param);
+
+int tlv_unset_tx_param(unsigned char type)
+{
+ struct tlv_rx_param *tprx;
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ tprx = tlv_deref_rx_params(type);
+
+ if (!tprx->class)
+ ret = __tlv_unset_param(type);
+ else
+ ret = __tlv_set_param(type, tprx, &tlv_default_param.tx_params);
+
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_unset_tx_param);
+
+int tlv_set_tx_param(unsigned char type, struct tlv_tx_param *tx_param_tmpl)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+
+ ret = __tlv_set_param(type, tlv_deref_rx_params(type), tx_param_tmpl);
+
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_set_tx_param);
+
+struct tlv_init_params {
+ int type;
+ struct tlv_tx_param t;
+ struct tlv_rx_param r;
+};
+
+static const struct tlv_init_params tlv_init_params[] __initconst = {
{
- .type = IPV6_TLV_ROUTERALERT,
- .func = ipv6_hop_ra,
+ .type = IPV6_TLV_HAO,
+
+ .t.preferred_order = TLV_PREF_ORDER_HAO,
+ .t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .t.user_perm = IPV6_TLV_PERM_NONE,
+ .t.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
+ .t.align_mult = (8 - 1), /* Align to 8n + 6 */
+ .t.align_off = 6,
+ .t.min_data_len = 16,
+ .t.max_data_len = 16,
+ .t.data_len_mult = (1 - 1), /* Fixed length */
+ .t.data_len_off = 0,
+
+ .r.func = ipv6_dest_hao,
+ .r.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
},
{
- .type = IPV6_TLV_JUMBO,
- .func = ipv6_hop_jumbo,
+ .type = IPV6_TLV_ROUTERALERT,
+
+ .t.preferred_order = TLV_PREF_ORDER_ROUTERALERT,
+ .t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .t.user_perm = IPV6_TLV_PERM_NONE,
+ .t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .t.align_mult = (2 - 1), /* Align to 2n */
+ .t.align_off = 0,
+ .t.min_data_len = 2,
+ .t.max_data_len = 2,
+ .t.data_len_mult = (1 - 1), /* Fixed length */
+ .t.data_len_off = 0,
+
+ .r.func = ipv6_hop_ra,
+ .r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
},
{
- .type = IPV6_TLV_CALIPSO,
- .func = ipv6_hop_calipso,
+ .type = IPV6_TLV_JUMBO,
+
+ .t.preferred_order = TLV_PREF_ORDER_JUMBO,
+ .t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .t.user_perm = IPV6_TLV_PERM_NONE,
+ .t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .t.align_mult = (4 - 1), /* Align to 4n + 2 */
+ .t.align_off = 2,
+ .t.min_data_len = 4,
+ .t.max_data_len = 4,
+ .t.data_len_mult = (1 - 1), /* Fixed length */
+ .t.data_len_off = 0,
+
+ .r.func = ipv6_hop_jumbo,
+ .r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
},
- { -1, }
+ {
+ .type = IPV6_TLV_CALIPSO,
+
+ .t.preferred_order = TLV_PREF_ORDER_CALIPSO,
+ .t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .t.user_perm = IPV6_TLV_PERM_NONE,
+ .t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .t.align_mult = (4 - 1), /* Align to 4n + 2 */
+ .t.align_off = 2,
+ .t.min_data_len = 8,
+ .t.max_data_len = 252,
+ .t.data_len_mult = (4 - 1), /* Length is multiple of 4 */
+ .t.data_len_off = 0,
+
+ .r.func = ipv6_hop_calipso,
+ .r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ }
};
+
+static int __init exthdrs_init(void)
+{
+ unsigned long check_map[BITS_TO_LONGS(256)];
+ struct tlv_param_table *tpt;
+ size_t tsize;
+ int i;
+
+ memset(check_map, 0, sizeof(check_map));
+
+ tsize = tlv_param_table_size(ARRAY_SIZE(tlv_init_params) + 1);
+
+ tpt = vmalloc(tsize);
+ if (!tpt)
+ return -ENOMEM;
+
+ memset(tpt, 0, tsize);
+
+ /* Zeroth TLV parameter entry is default */
+ tpt->params[0] = tlv_default_param;
+
+ for (i = 0; i < ARRAY_SIZE(tlv_init_params); i++) {
+ const struct tlv_init_params *tpi = &tlv_init_params[i];
+ unsigned int order = tpi->t.preferred_order;
+ struct tlv_param *tp = &tpt->params[i + 1];
+
+ WARN_ON(tpi->type < 2); /* Padding TLV initialized? */
+
+ if (order) {
+ WARN_ON(test_bit(order, check_map));
+ set_bit(order, check_map);
+ tp->tx_params = tpi->t;
+ } else {
+ tp->tx_params = tlv_default_param.tx_params;
+ }
+
+ if (tpi->r.class)
+ tp->rx_params = tpi->r;
+ else
+ tp->rx_params = tlv_default_param.rx_params;
+
+ tpt->entries[tpi->type] = i + 1;
+ }
+
+ tpt->count = ARRAY_SIZE(tlv_init_params) + 1;
+
+ RCU_INIT_POINTER(tlv_param_table, tpt);
+
+ return 0;
+}
+module_init(exthdrs_init);
+
+static void __exit exthdrs_fini(void)
+{
+}
+module_exit(exthdrs_fini);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 net-next 1/5] exthdrs: Create exthdrs_options.c
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net>
Create exthdrs_options.c to hold code related to Hop-by-Hop and
Destination extension header options. Move related functions in
exthdrs.c to the new file.
---
include/net/ipv6.h | 8 ++
net/ipv6/Makefile | 2 +-
net/ipv6/exthdrs.c | 342 --------------------------------------------
net/ipv6/exthdrs_options.c | 346 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 355 insertions(+), 343 deletions(-)
create mode 100644 net/ipv6/exthdrs_options.c
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index daf8086..8abdcdb 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,6 +379,14 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
+struct tlvtype_proc {
+ int type;
+ bool (*func)(struct sk_buff *skb, int offset);
+};
+
+extern const struct tlvtype_proc tlvprocdestopt_lst[];
+extern const struct tlvtype_proc tlvprochopopt_lst[];
+
bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
const struct inet6_skb_parm *opt);
struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index e0026fa..72bd775 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
- udp_offload.o seg6.o fib6_notifier.o
+ udp_offload.o seg6.o fib6_notifier.o exthdrs_options.o
ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 20291c2..6dbacf1 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -43,7 +43,6 @@
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
-#include <net/calipso.h>
#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
@@ -55,19 +54,6 @@
#include <linux/uaccess.h>
-/*
- * Parsing tlv encoded headers.
- *
- * Parsing function "func" returns true, if parsing succeed
- * and false, if it failed.
- * It MUST NOT touch skb->h.
- */
-
-struct tlvtype_proc {
- int type;
- bool (*func)(struct sk_buff *skb, int offset);
-};
-
/*********************
Generic functions
*********************/
@@ -204,80 +190,6 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
return false;
}
-/*****************************
- Destination options header.
- *****************************/
-
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
-{
- struct ipv6_destopt_hao *hao;
- struct inet6_skb_parm *opt = IP6CB(skb);
- struct ipv6hdr *ipv6h = ipv6_hdr(skb);
- int ret;
-
- if (opt->dsthao) {
- net_dbg_ratelimited("hao duplicated\n");
- goto discard;
- }
- opt->dsthao = opt->dst1;
- opt->dst1 = 0;
-
- hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
-
- if (hao->length != 16) {
- net_dbg_ratelimited("hao invalid option length = %d\n",
- hao->length);
- goto discard;
- }
-
- if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
- net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
- &hao->addr);
- goto discard;
- }
-
- ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
- (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
- if (unlikely(ret < 0))
- goto discard;
-
- if (skb_cloned(skb)) {
- if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
- goto discard;
-
- /* update all variable using below by copied skbuff */
- hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
- optoff);
- ipv6h = ipv6_hdr(skb);
- }
-
- if (skb->ip_summed == CHECKSUM_COMPLETE)
- skb->ip_summed = CHECKSUM_NONE;
-
- swap(ipv6h->saddr, hao->addr);
-
- if (skb->tstamp == 0)
- __net_timestamp(skb);
-
- return true;
-
- discard:
- kfree_skb(skb);
- return false;
-}
-#endif
-
-static const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
- {
- .type = IPV6_TLV_HAO,
- .func = ipv6_dest_hao,
- },
-#endif
- {-1, NULL}
-};
-
static int ipv6_destopt_rcv(struct sk_buff *skb)
{
struct inet6_dev *idev = __in6_dev_get(skb->dev);
@@ -706,122 +618,6 @@ void ipv6_exthdrs_exit(void)
inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
}
-/**********************************
- Hop-by-hop options.
- **********************************/
-
-/*
- * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
- */
-static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
-{
- return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
-}
-
-static inline struct net *ipv6_skb_net(struct sk_buff *skb)
-{
- return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
-}
-
-/* Router Alert as of RFC 2711 */
-
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
-
- if (nh[optoff + 1] == 2) {
- IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
- memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
- return true;
- }
- net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
- nh[optoff + 1]);
- kfree_skb(skb);
- return false;
-}
-
-/* Jumbo payload */
-
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
- struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
- struct net *net = ipv6_skb_net(skb);
- u32 pkt_len;
-
- if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
- net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
- nh[optoff+1]);
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- goto drop;
- }
-
- pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
- if (pkt_len <= IPV6_MAXPLEN) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
- return false;
- }
- if (ipv6_hdr(skb)->payload_len) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
- return false;
- }
-
- if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
- __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
- goto drop;
- }
-
- if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
- goto drop;
-
- IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
- return true;
-
-drop:
- kfree_skb(skb);
- return false;
-}
-
-/* CALIPSO RFC 5570 */
-
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
-{
- const unsigned char *nh = skb_network_header(skb);
-
- if (nh[optoff + 1] < 8)
- goto drop;
-
- if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
- goto drop;
-
- if (!calipso_validate(skb, nh + optoff))
- goto drop;
-
- return true;
-
-drop:
- kfree_skb(skb);
- return false;
-}
-
-static const struct tlvtype_proc tlvprochopopt_lst[] = {
- {
- .type = IPV6_TLV_ROUTERALERT,
- .func = ipv6_hop_ra,
- },
- {
- .type = IPV6_TLV_JUMBO,
- .func = ipv6_hop_jumbo,
- },
- {
- .type = IPV6_TLV_CALIPSO,
- .func = ipv6_hop_calipso,
- },
- { -1, }
-};
-
int ipv6_parse_hopopts(struct sk_buff *skb)
{
struct inet6_skb_parm *opt = IP6CB(skb);
@@ -992,144 +788,6 @@ void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *pr
}
EXPORT_SYMBOL(ipv6_push_frag_opts);
-struct ipv6_txoptions *
-ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
-{
- struct ipv6_txoptions *opt2;
-
- opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
- if (opt2) {
- long dif = (char *)opt2 - (char *)opt;
- memcpy(opt2, opt, opt->tot_len);
- if (opt2->hopopt)
- *((char **)&opt2->hopopt) += dif;
- if (opt2->dst0opt)
- *((char **)&opt2->dst0opt) += dif;
- if (opt2->dst1opt)
- *((char **)&opt2->dst1opt) += dif;
- if (opt2->srcrt)
- *((char **)&opt2->srcrt) += dif;
- refcount_set(&opt2->refcnt, 1);
- }
- return opt2;
-}
-EXPORT_SYMBOL_GPL(ipv6_dup_options);
-
-static void ipv6_renew_option(int renewtype,
- struct ipv6_opt_hdr **dest,
- struct ipv6_opt_hdr *old,
- struct ipv6_opt_hdr *new,
- int newtype, char **p)
-{
- struct ipv6_opt_hdr *src;
-
- src = (renewtype == newtype ? new : old);
- if (!src)
- return;
-
- memcpy(*p, src, ipv6_optlen(src));
- *dest = (struct ipv6_opt_hdr *)*p;
- *p += CMSG_ALIGN(ipv6_optlen(*dest));
-}
-
-/**
- * ipv6_renew_options - replace a specific ext hdr with a new one.
- *
- * @sk: sock from which to allocate memory
- * @opt: original options
- * @newtype: option type to replace in @opt
- * @newopt: new option of type @newtype to replace (user-mem)
- * @newoptlen: length of @newopt
- *
- * Returns a new set of options which is a copy of @opt with the
- * option type @newtype replaced with @newopt.
- *
- * @opt may be NULL, in which case a new set of options is returned
- * containing just @newopt.
- *
- * @newopt may be NULL, in which case the specified option type is
- * not copied into the new set of options.
- *
- * The new set of options is allocated from the socket option memory
- * buffer of @sk.
- */
-struct ipv6_txoptions *
-ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
- int newtype, struct ipv6_opt_hdr *newopt)
-{
- int tot_len = 0;
- char *p;
- struct ipv6_txoptions *opt2;
-
- if (opt) {
- if (newtype != IPV6_HOPOPTS && opt->hopopt)
- tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
- if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
- tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
- if (newtype != IPV6_RTHDR && opt->srcrt)
- tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
- if (newtype != IPV6_DSTOPTS && opt->dst1opt)
- tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
- }
-
- if (newopt)
- tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
-
- if (!tot_len)
- return NULL;
-
- tot_len += sizeof(*opt2);
- opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
- if (!opt2)
- return ERR_PTR(-ENOBUFS);
-
- memset(opt2, 0, tot_len);
- refcount_set(&opt2->refcnt, 1);
- opt2->tot_len = tot_len;
- p = (char *)(opt2 + 1);
-
- ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
- (opt ? opt->hopopt : NULL),
- newopt, newtype, &p);
- ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
- (opt ? opt->dst0opt : NULL),
- newopt, newtype, &p);
- ipv6_renew_option(IPV6_RTHDR,
- (struct ipv6_opt_hdr **)&opt2->srcrt,
- (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
- newopt, newtype, &p);
- ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
- (opt ? opt->dst1opt : NULL),
- newopt, newtype, &p);
-
- opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
- (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
- (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
- opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
-
- return opt2;
-}
-
-struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
- struct ipv6_txoptions *opt)
-{
- /*
- * ignore the dest before srcrt unless srcrt is being included.
- * --yoshfuji
- */
- if (opt && opt->dst0opt && !opt->srcrt) {
- if (opt_space != opt) {
- memcpy(opt_space, opt, sizeof(*opt_space));
- opt = opt_space;
- }
- opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
- opt->dst0opt = NULL;
- }
-
- return opt;
-}
-EXPORT_SYMBOL_GPL(ipv6_fixup_options);
-
/**
* fl6_update_dst - update flowi destination address with info given
* by srcrt option, if any.
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
new file mode 100644
index 0000000..70266a6
--- /dev/null
+++ b/net/ipv6/exthdrs_options.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/errno.h>
+#include <linux/in6.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/calipso.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+#include <net/xfrm.h>
+#endif
+
+/* Parsing tlv encoded headers.
+ *
+ * Parsing function "func" returns true, if parsing succeed
+ * and false, if it failed.
+ * It MUST NOT touch skb->h.
+ */
+
+struct ipv6_txoptions *
+ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
+{
+ struct ipv6_txoptions *opt2;
+
+ opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
+ if (opt2) {
+ long dif = (char *)opt2 - (char *)opt;
+
+ memcpy(opt2, opt, opt->tot_len);
+ if (opt2->hopopt)
+ *((char **)&opt2->hopopt) += dif;
+ if (opt2->dst0opt)
+ *((char **)&opt2->dst0opt) += dif;
+ if (opt2->dst1opt)
+ *((char **)&opt2->dst1opt) += dif;
+ if (opt2->srcrt)
+ *((char **)&opt2->srcrt) += dif;
+ refcount_set(&opt2->refcnt, 1);
+ }
+ return opt2;
+}
+EXPORT_SYMBOL_GPL(ipv6_dup_options);
+
+static void ipv6_renew_option(int renewtype,
+ struct ipv6_opt_hdr **dest,
+ struct ipv6_opt_hdr *old,
+ struct ipv6_opt_hdr *new,
+ int newtype, char **p)
+{
+ struct ipv6_opt_hdr *src;
+
+ src = (renewtype == newtype ? new : old);
+ if (!src)
+ return;
+
+ memcpy(*p, src, ipv6_optlen(src));
+ *dest = (struct ipv6_opt_hdr *)*p;
+ *p += CMSG_ALIGN(ipv6_optlen(*dest));
+}
+
+/**
+ * ipv6_renew_options - replace a specific ext hdr with a new one.
+ *
+ * @sk: sock from which to allocate memory
+ * @opt: original options
+ * @newtype: option type to replace in @opt
+ * @newopt: new option of type @newtype to replace (user-mem)
+ * @newoptlen: length of @newopt
+ *
+ * Returns a new set of options which is a copy of @opt with the
+ * option type @newtype replaced with @newopt.
+ *
+ * @opt may be NULL, in which case a new set of options is returned
+ * containing just @newopt.
+ *
+ * @newopt may be NULL, in which case the specified option type is
+ * not copied into the new set of options.
+ *
+ * The new set of options is allocated from the socket option memory
+ * buffer of @sk.
+ */
+struct ipv6_txoptions *
+ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
+ int newtype, struct ipv6_opt_hdr *newopt)
+{
+ int tot_len = 0;
+ char *p;
+ struct ipv6_txoptions *opt2;
+
+ if (opt) {
+ if (newtype != IPV6_HOPOPTS && opt->hopopt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
+ if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
+ if (newtype != IPV6_RTHDR && opt->srcrt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
+ if (newtype != IPV6_DSTOPTS && opt->dst1opt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
+ }
+
+ if (newopt)
+ tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
+
+ if (!tot_len)
+ return NULL;
+
+ tot_len += sizeof(*opt2);
+ opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
+ if (!opt2)
+ return ERR_PTR(-ENOBUFS);
+
+ memset(opt2, 0, tot_len);
+ refcount_set(&opt2->refcnt, 1);
+ opt2->tot_len = tot_len;
+ p = (char *)(opt2 + 1);
+
+ ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
+ (opt ? opt->hopopt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
+ (opt ? opt->dst0opt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_RTHDR,
+ (struct ipv6_opt_hdr **)&opt2->srcrt,
+ (opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
+ newopt, newtype, &p);
+ ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
+ (opt ? opt->dst1opt : NULL),
+ newopt, newtype, &p);
+
+ opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
+ (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
+ (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
+ opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
+
+ return opt2;
+}
+
+struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
+ struct ipv6_txoptions *opt)
+{
+ /* ignore the dest before srcrt unless srcrt is being included.
+ * --yoshfuji
+ */
+ if (opt && opt->dst0opt && !opt->srcrt) {
+ if (opt_space != opt) {
+ memcpy(opt_space, opt, sizeof(*opt_space));
+ opt = opt_space;
+ }
+ opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
+ opt->dst0opt = NULL;
+ }
+
+ return opt;
+}
+EXPORT_SYMBOL_GPL(ipv6_fixup_options);
+
+/* Destination options header */
+
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+{
+ struct ipv6_destopt_hao *hao;
+ struct inet6_skb_parm *opt = IP6CB(skb);
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ int ret;
+
+ if (opt->dsthao) {
+ net_dbg_ratelimited("hao duplicated\n");
+ goto discard;
+ }
+ opt->dsthao = opt->dst1;
+ opt->dst1 = 0;
+
+ hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
+
+ if (hao->length != 16) {
+ net_dbg_ratelimited("hao invalid option length = %d\n",
+ hao->length);
+ goto discard;
+ }
+
+ if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
+ net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
+ &hao->addr);
+ goto discard;
+ }
+
+ ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
+ (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
+ if (unlikely(ret < 0))
+ goto discard;
+
+ if (skb_cloned(skb)) {
+ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto discard;
+
+ /* update all variable using below by copied skbuff */
+ hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
+ optoff);
+ ipv6h = ipv6_hdr(skb);
+ }
+
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->ip_summed = CHECKSUM_NONE;
+
+ swap(ipv6h->saddr, hao->addr);
+
+ if (skb->tstamp == 0)
+ __net_timestamp(skb);
+
+ return true;
+
+ discard:
+ kfree_skb(skb);
+ return false;
+}
+#endif
+
+const struct tlvtype_proc tlvprocdestopt_lst[] = {
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+ {
+ .type = IPV6_TLV_HAO,
+ .func = ipv6_dest_hao,
+ },
+#endif
+ {-1, NULL}
+};
+
+/* Hop-by-hop options */
+
+/* Note: we cannot rely on skb_dst(skb) before we assign it in
+ * ip6_route_input().
+ */
+static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
+{
+ return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) :
+ __in6_dev_get(skb->dev);
+}
+
+static inline struct net *ipv6_skb_net(struct sk_buff *skb)
+{
+ return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
+}
+
+/* Router Alert as of RFC 2711 */
+
+static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+
+ if (nh[optoff + 1] == 2) {
+ IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
+ memcpy(&IP6CB(skb)->ra, nh + optoff + 2,
+ sizeof(IP6CB(skb)->ra));
+ return true;
+ }
+ net_dbg_ratelimited("%s: wrong RA length %d\n",
+ __func__, nh[optoff + 1]);
+ kfree_skb(skb);
+ return false;
+}
+
+/* Jumbo payload */
+
+static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+ struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
+ struct net *net = ipv6_skb_net(skb);
+ u32 pkt_len;
+
+ if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
+ net_dbg_ratelimited("%s: wrong jumbo opt length/alignment %d\n",
+ __func__, nh[optoff + 1]);
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ goto drop;
+ }
+
+ pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
+ if (pkt_len <= IPV6_MAXPLEN) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff + 2);
+ return false;
+ }
+ if (ipv6_hdr(skb)->payload_len) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
+ return false;
+ }
+
+ if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
+ __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
+ goto drop;
+ }
+
+ if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
+ goto drop;
+
+ IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
+ return true;
+
+drop:
+ kfree_skb(skb);
+ return false;
+}
+
+/* CALIPSO RFC 5570 */
+
+static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+{
+ const unsigned char *nh = skb_network_header(skb);
+
+ if (nh[optoff + 1] < 8)
+ goto drop;
+
+ if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
+ goto drop;
+
+ if (!calipso_validate(skb, nh + optoff))
+ goto drop;
+
+ return true;
+
+drop:
+ kfree_skb(skb);
+ return false;
+}
+
+const struct tlvtype_proc tlvprochopopt_lst[] = {
+ {
+ .type = IPV6_TLV_ROUTERALERT,
+ .func = ipv6_hop_ra,
+ },
+ {
+ .type = IPV6_TLV_JUMBO,
+ .func = ipv6_hop_jumbo,
+ },
+ {
+ .type = IPV6_TLV_CALIPSO,
+ .func = ipv6_hop_calipso,
+ },
+ { -1, }
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v2 net-next 0/5] ipv6: Rework infrastructure for TLV options in extension headers
From: Tom Herbert @ 2019-01-28 18:22 UTC (permalink / raw)
To: davem, netdev; +Cc: Tom Herbert
This patch set implements an infrastructure to allow fine grained
control over IPv6 Destination and Hop-by-Hop TLV options. This
includes being able to registers handlers for receiving options as
well as a set of parameters that sets permissions for who may send a
TLV option and the preferred format of a list of options.
The goal of the patch is to enable broader use cases of IPv6 options,
including those for which non-privileged users can send options. In
order to curtail misuse of options in such cases, a number of
requirements on how the option may be sent and formatted is
enforced.
Particular features are:
- A single TLV paramters table containing the information about each
TLV is defined. Lookups are simple offset accesses to the table
based on TLV type. Both receiving and sending properties of TLVs
are maintained in the table.
- Allow registration/deregistration of receive handlers for specific
TLVs.
- Describe the properties of sending different TLVs in a TLV parameter
table. The parameter table can be managed via netlink.
- Allow non-privileged users to send TLVs for which they have been
granted permission.
- Provide a deep validation of TLVs and TLV lists to enforce specific
limits and permission in order to thwart misuse of TLVs.
- Define a canonical format for sending TLVs that includes a
preferred order, option alignment, minimal padding between TLVs.
- Allow individual TLVs to be added or set in txoptions list on a
socket.
Tested: Write and read different TLVs on a socket via setsockopt
and getsockopt. Flow is add individual TLV, read back options,
set read option on new socket as a list. Verify options are
properly sent and received. Used a modified ip command in iproute2
to test managing the TLV parameter via netlink.
v2:
- Change the TLV parameters table to be an array of u8s as
opposed to pointers. Each entry gives the offset into
a second array of TLV parameters structures. This reduces
the memory footprint needed for the table but still allows
O(1) lookups on TLV type.
Tom Herbert (5):
exthdrs: Create exthdrs_options.c
exthdrs: Registration of TLV handlers and parameters
ip6tlvs: Add netlink interface
ip6tlvs: Validation of TX Destination and Hop-by-Hop options
ip6tlvs: API to set and remove individual TLVs from DO or HBH EH
include/net/ipv6.h | 82 ++
include/uapi/linux/in6.h | 58 ++
net/ipv6/Makefile | 2 +-
net/ipv6/datagram.c | 27 +-
net/ipv6/exthdrs.c | 389 +---------
net/ipv6/exthdrs_options.c | 1798 ++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ipv6_sockglue.c | 110 ++-
7 files changed, 2089 insertions(+), 377 deletions(-)
create mode 100644 net/ipv6/exthdrs_options.c
--
2.7.4
^ permalink raw reply
* Re: [PATCH v3 lora-next 3/4] dt-bindings: lora: sx125x: add clock bindings
From: Rob Herring @ 2019-01-28 18:21 UTC (permalink / raw)
To: Ben Whitten
Cc: Andreas Färber, David Miller, Mark Rutland, netdev,
devicetree, linux-kernel@vger.kernel.org, linux-lpwan
In-Reply-To: <20190124145309.21698-4-ben.whitten@lairdtech.com>
On Thu, Jan 24, 2019 at 8:54 AM Ben Whitten <ben.whitten@gmail.com> wrote:
>
> From: Ben Whitten <ben.whitten@gmail.com>
>
> The SX125x consumes a clock (FXOSC) ranging from 32 to 36 MHz as its main
> XTAL. It may also consume a clock for the TX synthesizer or DAC input clock
> (FCLK_IN).
> If the radio is coupled with an SX130x the radio is therefor operating in
> master mode and it may also provide a gated version of FXOSC clock for the
> concentrator. In this case the concentrator is expecting a 32 MHz input
> clock.
>
> In the example we connect fxosc to the "txco" clock source, represented by a
> fixed clock. The radio also provides a clock output "fclk_out" for
> consumption by the SX130x concentrator.
>
> Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
> ---
> v2 -> v3:
> * Name clock inputs and outputs
> * Add FCLK_IN
> v1 -> v2:
> * Fixed incorrect usage of clock cells
> * Fixed wording in commit and descriptions
> * Dropped enforced clock names
> ---
> .../bindings/net/lora/semtech,sx125x.yaml | 33 +++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/lora/semtech,sx125x.yaml b/Documentation/devicetree/bindings/net/lora/semtech,sx125x.yaml
> index fe2d2a23c28b..be45bcfd97be 100644
> --- a/Documentation/devicetree/bindings/net/lora/semtech,sx125x.yaml
> +++ b/Documentation/devicetree/bindings/net/lora/semtech,sx125x.yaml
> @@ -27,12 +27,41 @@ properties:
> description: The chip select on the SPI bus or radio number in concentrator
> , with radio A = 0 and radio B = 1.
>
> + clocks:
> + maxItems: 2
> + description: Input clock (FXOSC) provider with output ranging from 32 MHz
> + to 36 MHz. TX synthesizer or DAC input clock (FCLK_IN) provider with
> + output ranging from 32 MHz to 36 MHz.
> +
> + clock-names:
> + items:
> + - const: fxosc
> + - const: fclk_in
The example has a single clock. Is that valid? If so, then you need to
add minItems and maxItems.
> + description: Input clock (FXOSC), TX synthesizer or DAC input clock
> + (FCLK_IN)
> +
> + clock-output-names:
> + items:
> + - const: fclk_out
> + description: Output clock (FCLK_OUT) name, clock is gated version of the
> + input (FXOSC). Used in master mode operation.
> +
> + '#clock-cells':
> + const: 0
> +
> required:
> - compatible
> - reg
>
> examples:
> - |
> + tcxo: dummy32m {
> + compatible = "fixed-clock";
> + clock-frequency = <32000000>;
> + clock-output-names = "tcxo";
> + #clock-cells = <0>;
> + };
> +
> spi {
> #address-cells = <1>;
> #size-cells = <0>;
> @@ -40,5 +69,9 @@ examples:
> radio0: radio@0 {
> compatible = "semtech,sx1257";
> reg = <0>;
> + clocks = <&tcxo>;
> + clock-names = "fxosc";
> + clock-output-names = "fclk_out";
> + #clock-cells = <0>;
> };
> };
> --
> 2.17.1
>
^ permalink raw reply
* Re: [PATCH v3 lora-next 2/4] dt-bindings: lora: sx125x: add basic documentation
From: Rob Herring @ 2019-01-28 18:18 UTC (permalink / raw)
To: Ben Whitten
Cc: Andreas Färber, David Miller, Mark Rutland, netdev,
devicetree, linux-kernel@vger.kernel.org, linux-lpwan
In-Reply-To: <20190124145309.21698-3-ben.whitten@lairdtech.com>
On Thu, Jan 24, 2019 at 8:53 AM Ben Whitten <ben.whitten@gmail.com> wrote:
>
> From: Ben Whitten <ben.whitten@gmail.com>
>
> The SX125x family are IQ radio transceivers from Semtech configured over
> SPI, they are typically connected to an SX130x series concentrator however
> may be connected to a host directly.
>
> Required properties include the radio number of the host or concentrator
> bus.
>
> Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
> ---
> v2 -> v3:
> * Renamed example node name to be consistent with concentrator
> v1 -> v2:
> * Dropped spi-max-frequency binding
> * Wording changes to commit and descriptions
> * Added spi wrapper suggested by Rob H
> * Dual licence
> ---
> .../bindings/net/lora/semtech,sx125x.yaml | 44 +++++++++++++++++++
> 1 file changed, 44 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/lora/semtech,sx125x.yaml
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Want retouching for your photos?
From: Jennifer @ 2019-01-28 8:48 UTC (permalink / raw)
To: netdev
Did you receive my email from last week?
Do you have needs for retouching of your photos? We can do it white
background for your pictures.
If you need clipping path just let me know.
You can send 1 or 2 photos for testing to judge our quality.
Thanks,
Jennifer
^ permalink raw reply
* Re: [PATCH v3 lora-next 1/4] dt-bindings: lora: sx130x: add basic documentation
From: Rob Herring @ 2019-01-28 18:18 UTC (permalink / raw)
To: Ben Whitten
Cc: Andreas Färber, David Miller, Mark Rutland, netdev,
devicetree, linux-kernel@vger.kernel.org, linux-lpwan
In-Reply-To: <20190124145309.21698-2-ben.whitten@lairdtech.com>
On Thu, Jan 24, 2019 at 8:53 AM Ben Whitten <ben.whitten@gmail.com> wrote:
>
> From: Ben Whitten <ben.whitten@gmail.com>
>
> Add basic documentation in YAML format for the SX130x series concentrators
> from Semtech.
> Required is; the location on the SPI bus and the compatible string.
>
> Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
> ---
> v2 -> v3:
> * Corrected fallback notation
> * Limited radio node names from anything to 'radio@'
> v1 -> v2:
> * Dropped reset-gpios, radio-spi from required.
> * Dropped spi-max-frequency from binding.
> * Changed compatible to allow fallback from SX1308 to SX1301.
> * Wording changes in commit and descriptions.
> * Dual licence
> * Incorperate Robs comments on child node and spi wrapper
> ---
> .../bindings/net/lora/semtech,sx130x.yaml | 101 ++++++++++++++++++
> 1 file changed, 101 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/lora/semtech,sx130x.yaml
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH lora-next 01/11] dt-bindings: net: lora: sx130x: add power lut binding
From: Rob Herring @ 2019-01-28 18:13 UTC (permalink / raw)
To: Ben Whitten
Cc: Andreas Färber, linux-lpwan, David S. Miller, Mark Rutland,
netdev, devicetree, linux-kernel@vger.kernel.org
In-Reply-To: <20190128161306.27805-2-ben.whitten@lairdtech.com>
On Mon, Jan 28, 2019 at 10:13 AM Ben Whitten <ben.whitten@gmail.com> wrote:
>
> From: Ben Whitten <ben.whitten@gmail.com>
>
> Adding power lookup table for the concentrator specified in DT.
>
> These values are calibrated for the hardware and are unique to hardware
> designs and certifications.
>
> First byte is signed power in dBm measured at the rf connector.
> Second byte is unsigned value for DIGITAL gain.
> Third byte is unsigned value for PA gain, 2 bits only.
> Fourth byte is unsigned value for DAC gain, 2 bits only.
> Fifth byte is unsigned value for MIXER gain, 4 bits only.
>
> Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
> ---
> .../bindings/net/lora/semtech,sx130x.yaml | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/lora/semtech,sx130x.yaml b/Documentation/devicetree/bindings/net/lora/semtech,sx130x.yaml
> index 24fad1218735..187fa2090a0b 100644
> --- a/Documentation/devicetree/bindings/net/lora/semtech,sx130x.yaml
> +++ b/Documentation/devicetree/bindings/net/lora/semtech,sx130x.yaml
> @@ -37,6 +37,15 @@ properties:
> maxItems: 1
> description: A connection of the reset gpio line.
>
> + power-lut:
> + description: Power lookup table for the concentrator.
> + Consisting of maximum 16 entries, each entry having the following format;
> + First byte is signed power in dBm measured at the rf connector.
> + Second byte is unsigned value for DIGITAL gain.
> + Third byte is unsigned value for PA gain, 2 bits only.
> + Fourth byte is unsigned value for DAC gain, 2 bits only.
> + Fifth byte is unsigned value for MIXER gain, 4 bits only.
Would you expect this to be a common lora property? If not it should
have a vendor prefix.
Either way, non-standard properties need to reference a type. That
would be 'uint8-matrix' in this case. See the '$ref' in the examples.
Though, I guess I need to add a signed type.
We can write all the free form text above as a schema:
minItems: 1
maxItems: 16
items:
items:
- description: ...
range??
- description: ...
range??
- description: ...
maximum: 3
- description: ...
maximum: 3
- description: ...
maximum: 15
> +
> radio-spi:
> type: object
> description: The concentrator can have two radios connected which are
> @@ -83,6 +92,10 @@ examples:
> reset-gpios = <&pioB 27 GPIO_ACTIVE_HIGH>;
> spi-max-frequency = <8000000>;
>
> + power-lut = [ 00 00 00 03 09
> + 03 00 00 03 0C
> + 04 00 00 03 0D ];
You may need to write this with '/bits/ 8' syntax so you can bracket
each 5 byte entry. I don't think we can do that with [] notation.
Rob
^ permalink raw reply
* Re: [PATCH AUTOSEL 4.19 033/258] sctp: Fix SKB list traversal in sctp_intl_store_ordered().
From: Marcelo Ricardo Leitner @ 2019-01-28 18:09 UTC (permalink / raw)
To: Sasha Levin; +Cc: linux-kernel, stable, David S. Miller, linux-sctp, netdev
In-Reply-To: <20190128155924.51521-33-sashal@kernel.org>
On Mon, Jan 28, 2019 at 10:55:39AM -0500, Sasha Levin wrote:
> From: "David S. Miller" <davem@davemloft.net>
>
> [ Upstream commit e15e067d0656625c77c52b4e5f0cfbf0c0c3583f ]
>
> Same change as made to sctp_intl_store_reasm().
>
> To be fully correct, an iterator has an undefined value when something
> like skb_queue_walk() naturally terminates.
>
> This will actually matter when SKB queues are converted over to
> list_head.
>
> Formalize what this code ends up doing with the current
> implementation.
Hi Sasha,
This one too then.
Thanks,
Marcelo
^ permalink raw reply
* [PATCH net 5/5] qed: Fix stack out of bounds bug
From: Manish Chopra @ 2019-01-28 18:05 UTC (permalink / raw)
To: davem; +Cc: netdev, aelior, mkalderon
In-Reply-To: <20190128180508.9902-1-manishc@marvell.com>
KASAN reported following bug in qed_init_qm_get_idx_from_flags
due to inappropriate casting of "pq_flags". Fix the type of "pq_flags".
[ 196.624707] BUG: KASAN: stack-out-of-bounds in qed_init_qm_get_idx_from_flags+0x1a4/0x1b8 [qed]
[ 196.624712] Read of size 8 at addr ffff809b00bc7360 by task kworker/0:9/1712
[ 196.624714]
[ 196.624720] CPU: 0 PID: 1712 Comm: kworker/0:9 Not tainted 4.18.0-60.el8.aarch64+debug #1
[ 196.624723] Hardware name: To be filled by O.E.M. Saber/Saber, BIOS 0ACKL024 09/26/2018
[ 196.624733] Workqueue: events work_for_cpu_fn
[ 196.624738] Call trace:
[ 196.624742] dump_backtrace+0x0/0x2f8
[ 196.624745] show_stack+0x24/0x30
[ 196.624749] dump_stack+0xe0/0x11c
[ 196.624755] print_address_description+0x68/0x260
[ 196.624759] kasan_report+0x178/0x340
[ 196.624762] __asan_report_load_n_noabort+0x38/0x48
[ 196.624786] qed_init_qm_get_idx_from_flags+0x1a4/0x1b8 [qed]
[ 196.624808] qed_init_qm_info+0xec0/0x2200 [qed]
[ 196.624830] qed_resc_alloc+0x284/0x7e8 [qed]
[ 196.624853] qed_slowpath_start+0x6cc/0x1ae8 [qed]
[ 196.624864] __qede_probe.isra.10+0x1cc/0x12c0 [qede]
[ 196.624874] qede_probe+0x78/0xf0 [qede]
[ 196.624879] local_pci_probe+0xc4/0x180
[ 196.624882] work_for_cpu_fn+0x54/0x98
[ 196.624885] process_one_work+0x758/0x1900
[ 196.624888] worker_thread+0x4e0/0xd18
[ 196.624892] kthread+0x2c8/0x350
[ 196.624897] ret_from_fork+0x10/0x18
[ 196.624899]
[ 196.624902] Allocated by task 2:
[ 196.624906] kasan_kmalloc.part.1+0x40/0x108
[ 196.624909] kasan_kmalloc+0xb4/0xc8
[ 196.624913] kasan_slab_alloc+0x14/0x20
[ 196.624916] kmem_cache_alloc_node+0x1dc/0x480
[ 196.624921] copy_process.isra.1.part.2+0x1d8/0x4a98
[ 196.624924] _do_fork+0x150/0xfa0
[ 196.624926] kernel_thread+0x48/0x58
[ 196.624930] kthreadd+0x3a4/0x5a0
[ 196.624932] ret_from_fork+0x10/0x18
[ 196.624934]
[ 196.624937] Freed by task 0:
[ 196.624938] (stack is not available)
[ 196.624940]
[ 196.624943] The buggy address belongs to the object at ffff809b00bc0000
[ 196.624943] which belongs to the cache thread_stack of size 32768
[ 196.624946] The buggy address is located 29536 bytes inside of
[ 196.624946] 32768-byte region [ffff809b00bc0000, ffff809b00bc8000)
[ 196.624948] The buggy address belongs to the page:
[ 196.624952] page:ffff7fe026c02e00 count:1 mapcount:0 mapping:ffff809b4001c000 index:0x0 compound_mapcount: 0
[ 196.624960] flags: 0xfffff8000008100(slab|head)
[ 196.624967] raw: 0fffff8000008100 dead000000000100 dead000000000200 ffff809b4001c000
[ 196.624970] raw: 0000000000000000 0000000000080008 00000001ffffffff 0000000000000000
[ 196.624973] page dumped because: kasan: bad access detected
[ 196.624974]
[ 196.624976] Memory state around the buggy address:
[ 196.624980] ffff809b00bc7200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 196.624983] ffff809b00bc7280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 196.624985] >ffff809b00bc7300: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 04 f2 f2 f2
[ 196.624988] ^
[ 196.624990] ffff809b00bc7380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 196.624993] ffff809b00bc7400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 196.624995] ==================================================================
Signed-off-by: Manish Chopra <manishc@marvell.com>
Signed-off-by: Ariel Elior <aelior@marvell.com>
---
drivers/net/ethernet/qlogic/qed/qed_dev.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 8f65514..2ecaaaa 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -795,19 +795,19 @@ static void qed_init_qm_pq(struct qed_hwfn *p_hwfn,
/* get pq index according to PQ_FLAGS */
static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn,
- u32 pq_flags)
+ unsigned long pq_flags)
{
struct qed_qm_info *qm_info = &p_hwfn->qm_info;
/* Can't have multiple flags set here */
- if (bitmap_weight((unsigned long *)&pq_flags,
+ if (bitmap_weight(&pq_flags,
sizeof(pq_flags) * BITS_PER_BYTE) > 1) {
- DP_ERR(p_hwfn, "requested multiple pq flags 0x%x\n", pq_flags);
+ DP_ERR(p_hwfn, "requested multiple pq flags 0x%lx\n", pq_flags);
goto err;
}
if (!(qed_get_pq_flags(p_hwfn) & pq_flags)) {
- DP_ERR(p_hwfn, "pq flag 0x%x is not set\n", pq_flags);
+ DP_ERR(p_hwfn, "pq flag 0x%lx is not set\n", pq_flags);
goto err;
}
--
1.8.3.1
^ permalink raw reply related
* [PATCH net 4/5] qed: Fix system crash in ll2 xmit
From: Manish Chopra @ 2019-01-28 18:05 UTC (permalink / raw)
To: davem; +Cc: netdev, aelior, mkalderon
In-Reply-To: <20190128180508.9902-1-manishc@marvell.com>
Cache number of fragments in the skb locally as in case
of linear skb (with zero fragments), tx completion
(or freeing of skb) may happen before driver tries
to get number of frgaments from the skb which could
lead to stale access to an already freed skb.
Signed-off-by: Manish Chopra <manishc@marvell.com>
Signed-off-by: Ariel Elior <aelior@marvell.com>
---
drivers/net/ethernet/qlogic/qed/qed_ll2.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
index d9237c6..b5f419b 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
@@ -2451,19 +2451,24 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
{
struct qed_ll2_tx_pkt_info pkt;
const skb_frag_t *frag;
+ u8 flags = 0, nr_frags;
int rc = -EINVAL, i;
dma_addr_t mapping;
u16 vlan = 0;
- u8 flags = 0;
if (unlikely(skb->ip_summed != CHECKSUM_NONE)) {
DP_INFO(cdev, "Cannot transmit a checksummed packet\n");
return -EINVAL;
}
- if (1 + skb_shinfo(skb)->nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
+ /* Cache number of fragments from SKB since SKB may be freed by
+ * the completion routine after calling qed_ll2_prepare_tx_packet()
+ */
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ if (1 + nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
DP_ERR(cdev, "Cannot transmit a packet with %d fragments\n",
- 1 + skb_shinfo(skb)->nr_frags);
+ 1 + nr_frags);
return -EINVAL;
}
@@ -2485,7 +2490,7 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
}
memset(&pkt, 0, sizeof(pkt));
- pkt.num_of_bds = 1 + skb_shinfo(skb)->nr_frags;
+ pkt.num_of_bds = 1 + nr_frags;
pkt.vlan = vlan;
pkt.bd_flags = flags;
pkt.tx_dest = QED_LL2_TX_DEST_NW;
@@ -2496,12 +2501,17 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
test_bit(QED_LL2_XMIT_FLAGS_FIP_DISCOVERY, &xmit_flags))
pkt.remove_stag = true;
+ /* qed_ll2_prepare_tx_packet() may actually send the packet if
+ * there are no fragments in the skb and subsequently the completion
+ * routine may run and free the SKB, so no dereferencing the SKB
+ * beyond this point unless skb has any fragments.
+ */
rc = qed_ll2_prepare_tx_packet(&cdev->hwfns[0], cdev->ll2->handle,
&pkt, 1);
if (rc)
goto err;
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ for (i = 0; i < nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
mapping = skb_frag_dma_map(&cdev->pdev->dev, frag, 0,
--
1.8.3.1
^ permalink raw reply related
* [PATCH net 2/5] qed: Fix LACP pdu drops for VFs
From: Manish Chopra @ 2019-01-28 18:05 UTC (permalink / raw)
To: davem; +Cc: netdev, aelior, mkalderon
In-Reply-To: <20190128180508.9902-1-manishc@marvell.com>
VF is always configured to drop control frames
(with reserved mac addresses) but to work LACP
on the VFs, it would require LACP control frames
to be forwarded or transmitted successfully.
This patch fixes this in such a way that trusted VFs
(marked through ndo_set_vf_trust) would be allowed to
pass the control frames such as LACP pdus.
Signed-off-by: Manish Chopra <manishc@marvell.com>
Signed-off-by: Ariel Elior <aelior@marvell.com>
---
drivers/net/ethernet/qlogic/qed/qed_l2.c | 5 +++++
drivers/net/ethernet/qlogic/qed/qed_l2.h | 3 +++
drivers/net/ethernet/qlogic/qed/qed_sriov.c | 10 ++++++++--
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c
index b8baa6fc..e68ca83 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_l2.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c
@@ -748,6 +748,11 @@ int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
return rc;
}
+ if (p_params->update_ctl_frame_check) {
+ p_cmn->ctl_frame_mac_check_en = p_params->mac_chk_en;
+ p_cmn->ctl_frame_ethtype_check_en = p_params->ethtype_chk_en;
+ }
+
/* Update mcast bins for VFs, PF doesn't use this functionality */
qed_sp_update_mcast_bin(p_hwfn, p_ramrod, p_params);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h
index 8d80f10..7127d5a 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_l2.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h
@@ -219,6 +219,9 @@ struct qed_sp_vport_update_params {
struct qed_rss_params *rss_params;
struct qed_filter_accept_flags accept_flags;
struct qed_sge_tpa_params *sge_tpa_params;
+ u8 update_ctl_frame_check;
+ u8 mac_chk_en;
+ u8 ethtype_chk_en;
};
int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
index ca6290f..71a7af1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c
@@ -1969,7 +1969,9 @@ static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn,
params.vport_id = vf->vport_id;
params.max_buffers_per_cqe = start->max_buffers_per_cqe;
params.mtu = vf->mtu;
- params.check_mac = true;
+
+ /* Non trusted VFs should enable control frame filtering */
+ params.check_mac = !vf->p_vf_info.is_trusted_configured;
rc = qed_sp_eth_vport_start(p_hwfn, ¶ms);
if (rc) {
@@ -5130,6 +5132,9 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
params.opaque_fid = vf->opaque_fid;
params.vport_id = vf->vport_id;
+ params.update_ctl_frame_check = 1;
+ params.mac_chk_en = !vf_info->is_trusted_configured;
+
if (vf_info->rx_accept_mode & mask) {
flags->update_rx_mode_config = 1;
flags->rx_accept_filter = vf_info->rx_accept_mode;
@@ -5147,7 +5152,8 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
}
if (flags->update_rx_mode_config ||
- flags->update_tx_mode_config)
+ flags->update_tx_mode_config ||
+ params.update_ctl_frame_check)
qed_sp_vport_update(hwfn, ¶ms,
QED_SPQ_MODE_EBLOCK, NULL);
}
--
1.8.3.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox