* [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I
@ 2019-04-09 19:09 Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 1/6] exthdrs: Create exthdrs_options.c Tom Herbert
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
Extension headers are the mechanism of extensibility for the IPv6
protocol, however to date they have only seen limited deployment.
The reasons for that are because intermediate devices don't handle
them well, and there haven't really be any useful extension headers
defined. In particular, Destination and Hop-by-Hop options have
not been deployed to any extent.
The landscape may be changing as there are now a number of serious
efforts to define and deploy extension headers. In particular, a number
of uses for Hop-by-Hop Options are currently being proposed, Some of
these are from router vendors so there is hope that they might start
start to fix their brokenness. These proposals include IOAM, Path MTU,
Firewall and Service Tickets, SRv6, CRH, etc.
Assuming that IPv6 extension headers gain traction, that leaves a
noticeable gap in IPv4 support. IPv4 options have long been considered a
non-starter for deployment. An alternative being proposed is to enable
use of IPv6 options with IPv4 (draft-herbert-ipv4-eh-00).
This series of patch sets endeavours to make extension headers and
related options useful and easy to use. The following items will be
addressed:
- Reorganize extension header files
- Allow registration of TLV handlers
- Elaborate on the TLV tables to include more characteristics
- Add a netlink interface to set TLV parameters (such as
alignment requirements, authorization to send, etc.)
- Enhance validation of TLVs being sent. Validation is strict
(unless overridden by admin) following that sending clause
of the robustness principle
- Allow non-privileged users to set Hop-by-Hop and Destination
Options if authorized by the admin
- Add an API that allows individual Hop-by-Hop and Destination
Options to be set or removed for a connected socket. The
backend end enforces permissions on what TLVs may be set and
merges set TLVs per following the rules in the TLV parameter table
(for instance, TLV parameters include a preferred sending order
that merging adheres to)
- Support for some of the aforementioned options
- Enable IPv4 extension headers
------
In this series:
- Create exhdrs_options.c. Move options specific processing to this
file from exthdrs.c (for RA, Jumbo, Calipso, and HAO)
- Allow modules to register TLV handlers for Destination and HBH
options.
- Add parameters block to TLV type entries that describe characteristics
related to the TLV. For the most part, these encode rules about
sending each TLV (TLV alignment requirements for instance).
- Add a netlink interface to manage parameters in the TLV table.
- Add validation of HBH and Destination Options that are set on a
socket or in ancillary data in sendmsg. The validation applies the
rules that are encoded in the TLV parameters.
- TLV parameters includes permissions that may allow non-privileged
users to set specific TLVs on a socket HBH options or Destination
options. Strong validation can be enabled for this to constrain
what the non-privileged user is able to do.
v2:
- Don't rename extension header files with IPv6 specific code before
code for IPv4 extension headers is present
- Added patches for creating TLV parameters and validation
Tested:
Set Hop-by-Hop options on TCP/UDP socket and verified to be functional.
Tom Herbert (6):
exthdrs: Create exthdrs_options.c
exthdrs: Move generic EH functions to exthdrs_core.c
exthdrs: Registration of TLV handlers and parameters
exthdrs: Add TX parameters
ip6tlvs: Add netlink interface
ip6tlvs: Validation of TX Destination and Hop-by-Hop options
include/net/ipv6.h | 109 +++++
include/uapi/linux/in6.h | 49 ++
net/ipv6/Makefile | 2 +-
net/ipv6/datagram.c | 30 +-
net/ipv6/exthdrs.c | 388 +--------------
net/ipv6/exthdrs_core.c | 1162 ++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/exthdrs_options.c | 271 +++++++++++
net/ipv6/ipv6_sockglue.c | 35 +-
8 files changed, 1643 insertions(+), 403 deletions(-)
create mode 100644 net/ipv6/exthdrs_options.c
--
2.7.4
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 1/6] exthdrs: Create exthdrs_options.c
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 2/6] exthdrs: Move generic EH functions to exthdrs_core.c Tom Herbert
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
Create exthdrs_options.c to hold code related to specific Hop-by-Hop
and Destination extension header options. Move related functions in
exthdrs.c to the new file.
---
include/net/ipv6.h | 15 ++++
net/ipv6/Makefile | 2 +-
net/ipv6/exthdrs.c | 204 ---------------------------------------------
net/ipv6/exthdrs_options.c | 201 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 217 insertions(+), 205 deletions(-)
create mode 100644 net/ipv6/exthdrs_options.c
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index daf8086..e36c2c1 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,6 +379,21 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
+/*
+ * 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);
+};
+
+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..55ca778 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);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
new file mode 100644
index 0000000..032e072
--- /dev/null
+++ b/net/ipv6/exthdrs_options.c
@@ -0,0 +1,201 @@
+// 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
+
+/* 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 [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 2/6] exthdrs: Move generic EH functions to exthdrs_core.c
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 1/6] exthdrs: Create exthdrs_options.c Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
Move generic functions in exthdrs.c to exthdrs_core.c so that exthdrs.c
only contains functions that are specific to IPv6 processing, and
exthdrs_core.c contains functions that are generic.
---
net/ipv6/exthdrs.c | 138 -----------------------------------------------
net/ipv6/exthdrs_core.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 139 insertions(+), 138 deletions(-)
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 55ca778..6dbacf1 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -788,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_core.c b/net/ipv6/exthdrs_core.c
index ae365df..e875f1a 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -279,3 +279,142 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
return nexthdr;
}
EXPORT_SYMBOL(ipv6_find_hdr);
+
+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;
+}
+EXPORT_SYMBOL(ipv6_renew_options);
+
+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);
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 1/6] exthdrs: Create exthdrs_options.c Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 2/6] exthdrs: Move generic EH functions to exthdrs_core.c Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-11 19:55 ` kbuild test robot
` (2 more replies)
2019-04-09 19:09 ` [PATCH v2 net-next 4/6] exthdrs: Add TX parameters Tom Herbert
` (2 subsequent siblings)
5 siblings, 3 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
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 proc data
structures which follows the array of u8s. The TLV proc data structure
contains parameters and handler functions for receiving and transmitting
TLVs. The zeroth element in the TLV proc array provides default
parameters for TLVs.
A class attribute 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).
Functions are defined to manipulate entries in the TLV parameter table.
* tlv_{set|unset}_tlvproc set a TLV proc entry,
* tlv_{set|unset}_tlvparams
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. An init table containing parameters for
TLVs supported by the kernel is used to initialize the TLV table.
---
include/net/ipv6.h | 57 ++++++++-
include/uapi/linux/in6.h | 10 ++
net/ipv6/exthdrs.c | 46 ++++----
net/ipv6/exthdrs_core.c | 279 +++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/exthdrs_options.c | 61 ++++++----
5 files changed, 406 insertions(+), 47 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e36c2c1..41da032 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -386,13 +386,60 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
* and false, if it failed.
* It MUST NOT touch skb->h.
*/
-struct tlvtype_proc {
- int type;
- bool (*func)(struct sk_buff *skb, int offset);
+struct tlv_ops {
+ bool (*func)(unsigned int class, struct sk_buff *skb, int offset);
};
-extern const struct tlvtype_proc tlvprocdestopt_lst[];
-extern const struct tlvtype_proc tlvprochopopt_lst[];
+struct tlv_params {
+ unsigned char rx_class : 3;
+};
+
+struct tlv_proc {
+ struct tlv_ops ops;
+ struct tlv_params params;
+};
+
+struct tlv_proc_init {
+ int type;
+ struct tlv_proc proc;
+};
+
+struct tlv_param_table_data {
+ unsigned char entries[256];
+ unsigned char count;
+ struct rcu_head rcu;
+ struct tlv_proc procs[0];
+};
+
+struct tlv_param_table {
+ struct tlv_param_table_data __rcu *data;
+};
+
+extern struct tlv_param_table ipv6_tlv_param_table;
+
+int tlv_set_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_proc *proc);
+int tlv_unset_proc(struct tlv_param_table *tlv_param_table, unsigned char type);
+int tlv_set_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_params *params);
+int tlv_unset_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type);
+
+int exthdrs_init(struct tlv_param_table *tlv_param_table,
+ const struct tlv_proc_init *init_params,
+ int num_init_params);
+void exthdrs_fini(struct tlv_param_table *tlv_param_table);
+
+/* tlv_get_proc assumes rcu_read_lock is held */
+static inline struct tlv_proc *tlv_get_proc(
+ struct tlv_param_table *tlv_param_table,
+ unsigned int type)
+{
+ struct tlv_param_table_data *tpt =
+ rcu_dereference(tlv_param_table->data);
+
+ return &tpt->procs[tpt->entries[type]];
+}
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 9f2273a..8b9ac7f 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -297,4 +297,14 @@ struct in6_flowlabel_req {
* ...
* MRT6_MAX
*/
+
+/* Flags for EH type that can use a TLV option */
+#define IPV6_TLV_CLASS_FLAG_HOPOPT BIT(0)
+#define IPV6_TLV_CLASS_FLAG_RTRDSTOPT BIT(1)
+#define IPV6_TLV_CLASS_FLAG_DSTOPT BIT(2)
+#define IPV6_TLV_CLASS_MAX ((1 << 3) - 1)
+
+#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..b28c108 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;
+ const struct tlv_proc *curr;
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_lock();
+
while (len > 0) {
int optlen = nh[off + 1] + 2;
int i;
@@ -162,19 +165,18 @@ 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;
- }
+ curr = tlv_get_proc(&ipv6_tlv_param_table, nh[off]);
+ if ((curr->params.rx_class & class) && curr->ops.func) {
+ /* Handler will apply additional checks to
+ * the TLV
+ */
+ if (!curr->ops.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 +185,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 +226,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 +649,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_core.c b/net/ipv6/exthdrs_core.c
index e875f1a..aaa4582 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -418,3 +418,282 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
return opt;
}
EXPORT_SYMBOL_GPL(ipv6_fixup_options);
+
+/* TLV parameter table functions and structures */
+
+static void tlv_param_table_release(struct rcu_head *rcu)
+{
+ struct tlv_param_table_data *tpt =
+ container_of(rcu, struct tlv_param_table_data, rcu);
+
+ vfree(tpt);
+}
+
+/* Default (unset) values for TLV parameters */
+static const struct tlv_proc tlv_default_proc = {
+ .params.rx_class = 0,
+};
+
+static size_t tlv_param_table_size(unsigned char count)
+{
+ return sizeof(struct tlv_param_table_data) +
+ (count * sizeof(struct tlv_proc));
+}
+
+static DEFINE_MUTEX(tlv_mutex);
+
+/* mutex held */
+static int __tlv_set_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_ops *ops,
+ const struct tlv_params *params)
+{
+ struct tlv_param_table_data *tpt, *old;
+ struct tlv_proc *tproc;
+ unsigned char count, pos;
+
+ old = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+
+ if (old->entries[type]) {
+ /* Type is already set, modifying entry */
+ pos = old->entries[type];
+ count = old->count;
+
+ /* If ops is not provided, take them from existing proc */
+ if (!ops)
+ ops = &old->procs[pos].ops;
+ } else {
+ /* Type entry unset, need to create new entry */
+ 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));
+
+ tproc = &tpt->procs[pos];
+ tproc->params = *params;
+ tproc->ops = ops ? *ops : tlv_default_proc.ops;
+
+ tpt->entries[type] = pos;
+ tpt->count = count;
+
+ rcu_assign_pointer(tlv_param_table->data, tpt);
+
+ call_rcu(&old->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+/* mutex held */
+static int __tlv_unset_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ struct tlv_param_table_data *tpt, *old;
+ unsigned char pos;
+ int i;
+
+ old = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+
+ if (!old->entries[type]) {
+ /* Type entry already unset, nothing to do */
+ return 0;
+ }
+
+ tpt = vmalloc(tlv_param_table_size(old->count - 1));
+ if (!tpt)
+ return -ENOMEM;
+
+ pos = old->entries[type];
+
+ memcpy(tpt->procs, old->procs, pos * sizeof(struct tlv_proc));
+ memcpy(&tpt->procs[pos], &old->procs[pos + 1],
+ (old->count - pos - 1) * sizeof(struct tlv_proc));
+
+ 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];
+ }
+
+ /* Clear entry for type being unset (point to default params) */
+ tpt->entries[type] = 0;
+
+ tpt->count = old->count - 1;
+
+ rcu_assign_pointer(tlv_param_table->data, tpt);
+
+ call_rcu(&old->rcu, tlv_param_table_release);
+
+ return 0;
+}
+
+static void __tlv_destroy_param_table(struct tlv_param_table *tlv_param_table)
+{
+ struct tlv_param_table_data *tpt;
+
+ mutex_lock(&tlv_mutex);
+
+ tpt = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+ if (tpt) {
+ rcu_assign_pointer(tlv_param_table->data, NULL);
+ call_rcu(&tpt->rcu, tlv_param_table_release);
+ }
+
+ mutex_unlock(&tlv_mutex);
+}
+
+int tlv_set_proc(struct tlv_param_table *tlv_param_table, unsigned char type,
+ const struct tlv_proc *proc)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+ ret = __tlv_set_proc(tlv_param_table, type, &proc->ops,
+ &proc->params);
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_set_proc);
+
+int tlv_unset_proc(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+ ret = __tlv_unset_proc(tlv_param_table, type);
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_unset_proc);
+
+int tlv_set_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type, const struct tlv_params *params)
+{
+ int ret;
+
+ if (type < 2)
+ return -EINVAL;
+
+ mutex_lock(&tlv_mutex);
+ ret = __tlv_set_proc(tlv_param_table, type, NULL, params);
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_set_params);
+
+/* tlv_internal_proc_type is used to check it the TLV proc was set
+ * internally. This is deduced by checking if any operations are
+ * defined.
+ */
+static bool tlv_internal_proc_type(struct tlv_proc *proc)
+{
+ return !!proc->ops.func;
+}
+
+int tlv_unset_params(struct tlv_param_table *tlv_param_table,
+ unsigned char type)
+{
+ struct tlv_param_table_data *tpt;
+ struct tlv_proc *proc;
+ int entry, ret = 0;
+
+ mutex_lock(&tlv_mutex);
+
+ tpt = rcu_dereference_protected(tlv_param_table->data,
+ lockdep_is_held(&tlv_mutex));
+ if (!tpt)
+ goto out;
+
+ entry = tpt->entries[type];
+ if (!entry) {
+ /* Entry is already unset */
+ goto out;
+ }
+
+ proc = &tpt->procs[entry];
+
+ if (tlv_internal_proc_type(proc)) {
+ /* TLV was set by internal source, so maintain
+ * the non-parameter fields (i.e. the operations).
+ */
+ ret = __tlv_set_proc(tlv_param_table, type, &proc->ops,
+ &tlv_default_proc.params);
+ } else {
+ ret = __tlv_unset_proc(tlv_param_table, type);
+ }
+
+out:
+ mutex_unlock(&tlv_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(tlv_unset_params);
+
+int exthdrs_init(struct tlv_param_table *tlv_param_table,
+ const struct tlv_proc_init *tlv_init_params,
+ int num_init_params)
+{
+ struct tlv_param_table_data *tpt;
+ size_t tsize;
+ int i;
+
+ tsize = tlv_param_table_size(num_init_params + 1);
+
+ tpt = vmalloc(tsize);
+ if (!tpt)
+ return -ENOMEM;
+
+ memset(tpt, 0, tsize);
+
+ /* Zeroth TLV proc entry is default */
+ tpt->procs[0] = tlv_default_proc;
+
+ for (i = 0; i < num_init_params; i++) {
+ const struct tlv_proc_init *tpi = &tlv_init_params[i];
+ struct tlv_proc *tp = &tpt->procs[i + 1];
+
+ if (WARN_ON(tpi->type < 2)) {
+ /* Padding TLV initialized? */
+ vfree(tpt);
+ return -EINVAL;
+ }
+ if (WARN_ON(tpt->entries[tpi->type])) {
+ /* TLV type already set */
+ vfree(tpt);
+ return -EINVAL;
+ }
+
+ *tp = tpi->proc;
+ tpt->entries[tpi->type] = i + 1;
+ }
+
+ tpt->count = num_init_params + 1;
+
+ RCU_INIT_POINTER(tlv_param_table->data, tpt);
+
+ return 0;
+}
+EXPORT_SYMBOL(exthdrs_init);
+
+void exthdrs_fini(struct tlv_param_table *tlv_param_table)
+{
+ __tlv_destroy_param_table(tlv_param_table);
+}
+EXPORT_SYMBOL(exthdrs_fini);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 032e072..9be82ef 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -15,7 +15,7 @@
/* 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);
@@ -74,16 +74,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
@@ -102,7 +92,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);
@@ -120,7 +110,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);
@@ -164,7 +154,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);
@@ -184,18 +175,44 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
return false;
}
-const struct tlvtype_proc tlvprochopopt_lst[] = {
+static const struct tlv_proc_init tlv_init_params[] __initconst = {
{
- .type = IPV6_TLV_ROUTERALERT,
- .func = ipv6_hop_ra,
+ .type = IPV6_TLV_HAO,
+
+ .proc.ops.func = ipv6_dest_hao,
+ .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_DSTOPT,
},
{
- .type = IPV6_TLV_JUMBO,
- .func = ipv6_hop_jumbo,
+ .type = IPV6_TLV_ROUTERALERT,
+
+ .proc.ops.func = ipv6_hop_ra,
+ .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
},
{
- .type = IPV6_TLV_CALIPSO,
- .func = ipv6_hop_calipso,
+ .type = IPV6_TLV_JUMBO,
+
+ .proc.ops.func = ipv6_hop_jumbo,
+ .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ },
+ {
+ .type = IPV6_TLV_CALIPSO,
+
+ .proc.ops.func = ipv6_hop_calipso,
+ .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
},
- { -1, }
};
+
+struct tlv_param_table __rcu ipv6_tlv_param_table;
+
+static int __init ipv6_exthdrs_init(void)
+{
+ return exthdrs_init(&ipv6_tlv_param_table, tlv_init_params,
+ ARRAY_SIZE(tlv_init_params));
+}
+module_init(ipv6_exthdrs_init);
+
+static void __exit ipv6_exthdrs_fini(void)
+{
+ exthdrs_fini(&ipv6_tlv_param_table);
+}
+module_exit(ipv6_exthdrs_fini);
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 4/6] exthdrs: Add TX parameters
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
` (2 preceding siblings ...)
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 5/6] ip6tlvs: Add netlink interface Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options Tom Herbert
5 siblings, 0 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
Define a number of transmit parameters for TLV Parameter table
definitions. These will be used for validating TLVs that are set
on a socket.
---
include/net/ipv6.h | 26 ++++++++++++++++++++-
include/uapi/linux/in6.h | 8 +++++++
net/ipv6/exthdrs.c | 2 +-
net/ipv6/exthdrs_core.c | 22 +++++++++++++++++-
net/ipv6/exthdrs_options.c | 57 ++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 108 insertions(+), 7 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 41da032..9b25d08 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -390,8 +390,26 @@ struct tlv_ops {
bool (*func)(unsigned int class, struct sk_buff *skb, int offset);
};
+struct tlv_rx_params {
+ unsigned char class : 3;
+};
+
+struct tlv_tx_params {
+ 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;
+ unsigned char preferred_order;
+};
+
struct tlv_params {
- unsigned char rx_class : 3;
+ struct tlv_rx_params r;
+ struct tlv_tx_params t;
};
struct tlv_proc {
@@ -417,6 +435,12 @@ struct tlv_param_table {
extern struct tlv_param_table ipv6_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
+
int tlv_set_proc(struct tlv_param_table *tlv_param_table,
unsigned char type, const struct tlv_proc *proc);
int tlv_unset_proc(struct tlv_param_table *tlv_param_table, unsigned char type);
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 8b9ac7f..6a99ee1 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -307,4 +307,12 @@ struct in6_flowlabel_req {
#define IPV6_TLV_CLASS_ANY_DSTOPT (IPV6_TLV_CLASS_FLAG_RTRDSTOPT | \
IPV6_TLV_CLASS_FLAG_DSTOPT)
+/* 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
+};
+
#endif /* _UAPI_LINUX_IN6_H */
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index b28c108..520c2eb 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -166,7 +166,7 @@ static bool ip6_parse_tlv(unsigned int class, struct sk_buff *skb,
goto bad;
curr = tlv_get_proc(&ipv6_tlv_param_table, nh[off]);
- if ((curr->params.rx_class & class) && curr->ops.func) {
+ if ((curr->params.r.class & class) && curr->ops.func) {
/* Handler will apply additional checks to
* the TLV
*/
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index aaa4582..98b5bc1 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -431,7 +431,18 @@ static void tlv_param_table_release(struct rcu_head *rcu)
/* Default (unset) values for TLV parameters */
static const struct tlv_proc tlv_default_proc = {
- .params.rx_class = 0,
+ .params.r.class = 0,
+
+ .params.t.preferred_order = 0,
+ .params.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .params.t.user_perm = IPV6_TLV_PERM_NONE,
+ .params.t.class = 0,
+ .params.t.align_mult = (4 - 1), /* Default alignment: 4n + 2 */
+ .params.t.align_off = 2,
+ .params.t.min_data_len = 0,
+ .params.t.max_data_len = 255,
+ .params.t.data_len_mult = (1 - 1), /* No default length align */
+ .params.t.data_len_off = 0,
};
static size_t tlv_param_table_size(unsigned char count)
@@ -650,10 +661,13 @@ int exthdrs_init(struct tlv_param_table *tlv_param_table,
const struct tlv_proc_init *tlv_init_params,
int num_init_params)
{
+ unsigned long check_map[BITS_TO_LONGS(256)];
struct tlv_param_table_data *tpt;
size_t tsize;
int i;
+ memset(check_map, 0, sizeof(check_map));
+
tsize = tlv_param_table_size(num_init_params + 1);
tpt = vmalloc(tsize);
@@ -667,6 +681,7 @@ int exthdrs_init(struct tlv_param_table *tlv_param_table,
for (i = 0; i < num_init_params; i++) {
const struct tlv_proc_init *tpi = &tlv_init_params[i];
+ unsigned int order = tpi->proc.params.t.preferred_order;
struct tlv_proc *tp = &tpt->procs[i + 1];
if (WARN_ON(tpi->type < 2)) {
@@ -680,6 +695,11 @@ int exthdrs_init(struct tlv_param_table *tlv_param_table,
return -EINVAL;
}
+ if (order) {
+ WARN_ON(test_bit(order, check_map));
+ set_bit(order, check_map);
+ }
+
*tp = tpi->proc;
tpt->entries[tpi->type] = i + 1;
}
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 9be82ef..b316143 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -180,25 +180,74 @@ static const struct tlv_proc_init tlv_init_params[] __initconst = {
.type = IPV6_TLV_HAO,
.proc.ops.func = ipv6_dest_hao,
- .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_DSTOPT,
+
+ .proc.params.r.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
+
+ .proc.params.t.preferred_order = TLV_PREF_ORDER_HAO,
+ .proc.params.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .proc.params.t.user_perm = IPV6_TLV_PERM_NONE,
+ .proc.params.t.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
+ .proc.params.t.align_mult = (8 - 1), /* Align to 8n + 6 */
+ .proc.params.t.align_off = 6,
+ .proc.params.t.min_data_len = 16,
+ .proc.params.t.max_data_len = 16,
+ .proc.params.t.data_len_mult = (1 - 1), /* Fixed length */
+ .proc.params.t.data_len_off = 0,
},
{
.type = IPV6_TLV_ROUTERALERT,
.proc.ops.func = ipv6_hop_ra,
- .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+
+ .proc.params.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+
+ .proc.params.t.preferred_order = TLV_PREF_ORDER_ROUTERALERT,
+ .proc.params.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .proc.params.t.user_perm = IPV6_TLV_PERM_NONE,
+ .proc.params.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .proc.params.t.align_mult = (2 - 1), /* Align to 2n */
+ .proc.params.t.align_off = 0,
+ .proc.params.t.min_data_len = 2,
+ .proc.params.t.max_data_len = 2,
+ .proc.params.t.data_len_mult = (1 - 1), /* Fixed length */
+ .proc.params.t.data_len_off = 0,
+
},
{
.type = IPV6_TLV_JUMBO,
.proc.ops.func = ipv6_hop_jumbo,
- .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+
+ .proc.params.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+
+ .proc.params.t.preferred_order = TLV_PREF_ORDER_JUMBO,
+ .proc.params.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .proc.params.t.user_perm = IPV6_TLV_PERM_NONE,
+ .proc.params.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .proc.params.t.align_mult = (4 - 1), /* Align to 4n + 2 */
+ .proc.params.t.align_off = 2,
+ .proc.params.t.min_data_len = 4,
+ .proc.params.t.max_data_len = 4,
+ .proc.params.t.data_len_mult = (1 - 1), /* Fixed length */
+ .proc.params.t.data_len_off = 0,
},
{
.type = IPV6_TLV_CALIPSO,
.proc.ops.func = ipv6_hop_calipso,
- .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .proc.params.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+
+ .proc.params.t.preferred_order = TLV_PREF_ORDER_CALIPSO,
+ .proc.params.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+ .proc.params.t.user_perm = IPV6_TLV_PERM_NONE,
+ .proc.params.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+ .proc.params.t.align_mult = (4 - 1), /* Align to 4n + 2 */
+ .proc.params.t.align_off = 2,
+ .proc.params.t.min_data_len = 8,
+ .proc.params.t.max_data_len = 252,
+ .proc.params.t.data_len_mult = (4 - 1),
+ /* Length is multiple of 4 */
+ .proc.params.t.data_len_off = 0,
},
};
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 5/6] ip6tlvs: Add netlink interface
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
` (3 preceding siblings ...)
2019-04-09 19:09 ` [PATCH v2 net-next 4/6] exthdrs: Add TX parameters Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options Tom Herbert
5 siblings, 0 replies; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
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/net/ipv6.h | 9 +-
include/uapi/linux/in6.h | 31 ++++
net/ipv6/exthdrs_core.c | 362 ++++++++++++++++++++++++++++++++++++++++++++-
net/ipv6/exthdrs_options.c | 10 +-
4 files changed, 401 insertions(+), 11 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 9b25d08..3d2995f 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -17,6 +17,7 @@
#include <linux/hardirq.h>
#include <linux/jhash.h>
#include <linux/refcount.h>
+#include <net/genetlink.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
#include <net/flow.h>
@@ -450,9 +451,11 @@ int tlv_unset_params(struct tlv_param_table *tlv_param_table,
unsigned char type);
int exthdrs_init(struct tlv_param_table *tlv_param_table,
- const struct tlv_proc_init *init_params,
- int num_init_params);
-void exthdrs_fini(struct tlv_param_table *tlv_param_table);
+ const struct tlv_proc_init *tlv_init_params, int proto,
+ int num_init_params, struct genl_family *tlv_genl_family,
+ char *tlv_genl_name, int tlv_genl_version);
+void exthdrs_fini(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family);
/* tlv_get_proc assumes rcu_read_lock is held */
static inline struct tlv_proc *tlv_get_proc(
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 6a99ee1..1c79361 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -306,6 +306,37 @@ struct in6_flowlabel_req {
#define IPV6_TLV_CLASS_ANY_DSTOPT (IPV6_TLV_CLASS_FLAG_RTRDSTOPT | \
IPV6_TLV_CLASS_FLAG_DSTOPT)
+/* 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 {
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 98b5bc1..09799bcd 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -3,7 +3,9 @@
* not configured or static.
*/
#include <linux/export.h>
+#include <net/genetlink.h>
#include <net/ipv6.h>
+#include <uapi/linux/genetlink.h>
/*
* find out if nexthdr is a well-known extension header or a protocol
@@ -657,14 +659,341 @@ int tlv_unset_params(struct tlv_param_table *tlv_param_table,
}
EXPORT_SYMBOL(tlv_unset_params);
+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 tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ struct tlv_params new_params;
+ struct tlv_tx_params *tptx;
+ struct tlv_proc *tproc;
+ 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();
+
+ /* Base new parameters on existing ones */
+ tproc = tlv_get_proc(tlv_param_table, tlv_type);
+ new_params = tproc->params;
+
+ if (info->attrs[IPV6_TLV_ATTR_ORDER]) {
+ v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ORDER]);
+ if (v) {
+ for (i = 2; i < 256; i++) {
+ tproc = tlv_get_proc(tlv_param_table, i);
+ tptx = &tproc->params.t;
+
+ /* Preferred orders must be unique */
+ if (tptx->preferred_order == v &&
+ i != tlv_type) {
+ retv = -EALREADY;
+ goto out;
+ }
+ }
+ new_params.t.preferred_order = v;
+ }
+ }
+
+ if (!new_params.t.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;
+
+ tproc = tlv_get_proc(tlv_param_table, i);
+ tptx = &tproc->params.t;
+ 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_params.t.preferred_order = 255 - (pos - 1);
+ else
+ new_params.t.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_params.t.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_params.t.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_params.t.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_params.t.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_params.t.align_off = v;
+ }
+
+ if (info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN])
+ new_params.t.max_data_len =
+ nla_get_u8(info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]);
+
+ if (info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN])
+ new_params.t.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_params.t.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_params.t.data_len_off = v;
+ }
+
+ retv = tlv_set_params(tlv_param_table, tlv_type, &new_params);
+
+out:
+ rcu_read_unlock();
+ return retv;
+}
+
+static int tlv_nl_cmd_unset(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ 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_params(tlv_param_table, tlv_type);
+}
+
+static int tlv_fill_info(struct tlv_param_table *tlv_param_table,
+ int tlv_type, struct sk_buff *msg, bool admin)
+{
+ struct tlv_proc *tproc;
+ struct tlv_params *tp;
+ int ret = 0;
+
+ rcu_read_lock();
+
+ tproc = tlv_get_proc(tlv_param_table, tlv_type);
+ tp = &tproc->params;
+
+ if (nla_put_u8(msg, IPV6_TLV_ATTR_TYPE, tlv_type) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ORDER, tp->t.preferred_order) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_USER_PERM, tp->t.user_perm) ||
+ (admin && nla_put_u8(msg, IPV6_TLV_ATTR_ADMIN_PERM,
+ tp->t.admin_perm)) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_CLASS, tp->t.class) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_MULT, tp->t.align_mult + 1) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_OFF, tp->t.align_off) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_MIN_DATA_LEN, tp->t.min_data_len) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_MAX_DATA_LEN, tp->t.max_data_len) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_MULT,
+ tp->t.data_len_mult + 1) ||
+ nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_OFF, tp->t.data_len_off))
+ ret = -1;
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static int tlv_dump_info(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ 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_param_table, tlv_type, skb, admin) < 0) {
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+ }
+
+ genlmsg_end(skb, hdr);
+
+ return 0;
+}
+
+static int tlv_nl_cmd_get(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ 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_param_table, tlv_nl_family,
+ 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 tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family,
+ 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(tlv_param_table, tlv_nl_family, 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;
+}
+
+extern struct genl_family ipv6_tlv_nl_family;
+
+static int ipv6_tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info)
+{
+ return tlv_nl_cmd_set(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info)
+{
+ return tlv_nl_cmd_unset(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
+{
+ return tlv_nl_cmd_get(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, info);
+}
+
+static int ipv6_tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ return tlv_nl_dump(&ipv6_tlv_param_table, &ipv6_tlv_nl_family,
+ skb, cb);
+}
+
+static const struct genl_ops ipv6_tlv_nl_ops[] = {
+{
+ .cmd = IPV6_TLV_CMD_SET,
+ .doit = ipv6_tlv_nl_cmd_set,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPV6_TLV_CMD_UNSET,
+ .doit = ipv6_tlv_nl_cmd_unset,
+ .flags = GENL_ADMIN_PERM,
+},
+{
+ .cmd = IPV6_TLV_CMD_GET,
+ .doit = ipv6_tlv_nl_cmd_get,
+ .dumpit = ipv6_tlv_nl_dump,
+},
+};
+
int exthdrs_init(struct tlv_param_table *tlv_param_table,
- const struct tlv_proc_init *tlv_init_params,
- int num_init_params)
+ const struct tlv_proc_init *tlv_init_params, int proto,
+ int num_init_params, struct genl_family *tlv_nl_family,
+ char tlv_genl_name[], int tlv_genl_version)
{
unsigned long check_map[BITS_TO_LONGS(256)];
+ const struct genl_ops *tlv_nl_ops;
struct tlv_param_table_data *tpt;
- size_t tsize;
- int i;
+ size_t tsize, ops_size;
+ int i, ret;
+
+ switch (proto) {
+ case ETH_P_IPV6:
+ tlv_nl_ops = ipv6_tlv_nl_ops;
+ ops_size = ARRAY_SIZE(ipv6_tlv_nl_ops);
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
memset(check_map, 0, sizeof(check_map));
@@ -708,12 +1037,35 @@ int exthdrs_init(struct tlv_param_table *tlv_param_table,
RCU_INIT_POINTER(tlv_param_table->data, tpt);
+ memset(tlv_nl_family, 0, sizeof(*tlv_nl_family));
+
+ tlv_nl_family->hdrsize = 0;
+ strncpy(tlv_nl_family->name, tlv_genl_name, GENL_NAMSIZ);
+ tlv_nl_family->version = tlv_genl_version;
+ tlv_nl_family->maxattr = IPV6_TLV_ATTR_MAX;
+ tlv_nl_family->netnsok = true;
+ tlv_nl_family->module = THIS_MODULE;
+ tlv_nl_family->ops = tlv_nl_ops;
+ tlv_nl_family->policy = tlv_nl_policy;
+ tlv_nl_family->n_ops = ops_size;
+
+ ret = genl_register_family(tlv_nl_family);
+ if (ret < 0)
+ goto fail_genl_register;
+
return 0;
+
+fail_genl_register:
+ __tlv_destroy_param_table(tlv_param_table);
+
+ return ret;
}
EXPORT_SYMBOL(exthdrs_init);
-void exthdrs_fini(struct tlv_param_table *tlv_param_table)
+void exthdrs_fini(struct tlv_param_table *tlv_param_table,
+ struct genl_family *tlv_nl_family)
{
__tlv_destroy_param_table(tlv_param_table);
+ genl_unregister_family(tlv_nl_family);
}
EXPORT_SYMBOL(exthdrs_fini);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index b316143..607a8c2 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -6,6 +6,7 @@
#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)
@@ -251,17 +252,20 @@ static const struct tlv_proc_init tlv_init_params[] __initconst = {
},
};
+struct genl_family ipv6_tlv_nl_family __ro_after_init;
struct tlv_param_table __rcu ipv6_tlv_param_table;
static int __init ipv6_exthdrs_init(void)
{
- return exthdrs_init(&ipv6_tlv_param_table, tlv_init_params,
- ARRAY_SIZE(tlv_init_params));
+ return exthdrs_init(&ipv6_tlv_param_table, tlv_init_params, ETH_P_IPV6,
+ ARRAY_SIZE(tlv_init_params),
+ &ipv6_tlv_nl_family, IPV6_TLV_GENL_NAME,
+ IPV6_TLV_GENL_VERSION);
}
module_init(ipv6_exthdrs_init);
static void __exit ipv6_exthdrs_fini(void)
{
- exthdrs_fini(&ipv6_tlv_param_table);
+ exthdrs_fini(&ipv6_tlv_param_table, &ipv6_tlv_nl_family);
}
module_exit(ipv6_exthdrs_fini);
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
` (4 preceding siblings ...)
2019-04-09 19:09 ` [PATCH v2 net-next 5/6] ip6tlvs: Add netlink interface Tom Herbert
@ 2019-04-09 19:09 ` Tom Herbert
2019-04-11 19:58 ` kbuild test robot
5 siblings, 1 reply; 11+ messages in thread
From: Tom Herbert @ 2019-04-09 19:09 UTC (permalink / raw
To: davem, netdev; +Cc: Tom Herbert
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 only 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 | 20 +++
net/ipv6/datagram.c | 30 ++--
net/ipv6/exthdrs_core.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++
net/ipv6/ipv6_sockglue.c | 35 +----
4 files changed, 417 insertions(+), 40 deletions(-)
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 3d2995f..ec1a161 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -457,6 +457,19 @@ int exthdrs_init(struct tlv_param_table *tlv_param_table,
void exthdrs_fini(struct tlv_param_table *tlv_param_table,
struct genl_family *tlv_nl_family);
+int ipv6_opt_validate_tlvs(struct net *net,
+ struct tlv_param_table *tlv_param_table,
+ struct ipv6_opt_hdr *opt,
+ unsigned int optname, bool admin);
+int ipv6_opt_validate_single_tlv(struct net *net,
+ struct tlv_param_table *tlv_param_table,
+ unsigned int optname,
+ unsigned char *tlv, size_t len,
+ bool deleting, bool admin);
+int ipv6_opt_check_perm(struct net *net,
+ struct tlv_param_table *tlv_param_table,
+ struct sock *sk, int optname, bool admin);
+
/* tlv_get_proc assumes rcu_read_lock is held */
static inline struct tlv_proc *tlv_get_proc(
struct tlv_param_table *tlv_param_table,
@@ -473,6 +486,13 @@ bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
struct ipv6_txoptions *opt);
+struct ipv6_txoptions *txoptions_from_opt(struct sock *sk,
+ struct tlv_param_table
+ *tlv_param_table,
+ struct ipv6_txoptions *opt,
+ int optname, char __user *optval,
+ unsigned int optlen);
+
static inline bool ipv6_accept_ra(struct inet6_dev *idev)
{
/* If forwarding is enabled, RA are not accepted unless the special
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index ee4a4e5..d029da0 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -853,10 +853,14 @@ 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, &ipv6_tlv_param_table,
+ 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 +877,14 @@ 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, &ipv6_tlv_param_table,
+ 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 +906,14 @@ 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, &ipv6_tlv_param_table,
+ 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_core.c b/net/ipv6/exthdrs_core.c
index 09799bcd..2c721ee 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -421,6 +421,313 @@ 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_params *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 tlv_param_table *tlv_param_table,
+ 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_params *tptx;
+ int retc, ret = -EINVAL;
+ struct tlv_proc *tproc;
+ 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;
+
+ tproc = tlv_get_proc(tlv_param_table, tlv[offset]);
+ tptx = &tproc->params.t;
+
+ 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 tlv_param_table *tlv_param_table,
+ struct ipv6_opt_hdr *opt, unsigned int optname,
+ bool admin)
+{
+ return __ipv6_opt_validate_tlvs(net, tlv_param_table,
+ 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,
+ struct tlv_param_table *tlv_param_table,
+ unsigned int optname,
+ unsigned char *tlv, size_t len,
+ bool deleting, bool admin)
+{
+ struct tlv_tx_params *tptx;
+ struct tlv_proc *tproc;
+ unsigned int class;
+ bool deep_check;
+ int ret = 0;
+
+ class = optname_to_tlv_class(optname);
+
+ if (tlv[0] < 2)
+ return -EINVAL;
+
+ if (len < 2)
+ return -EINVAL;
+
+ if (tlv[1] + 2 != len)
+ return -EINVAL;
+
+ rcu_read_lock();
+
+ tproc = tlv_get_proc(tlv_param_table, tlv[0]);
+ tptx = &tproc->params.t;
+
+ 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 that current capabilities allows modifying
+ * 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 tlv_param_table *tlv_param_table,
+ 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, tlv_param_table, opt, optname,
+ true, admin);
+
+out:
+ txopt_put(old);
+ return retv;
+}
+EXPORT_SYMBOL(ipv6_opt_check_perm);
+
/* TLV parameter table functions and structures */
static void tlv_param_table_release(struct rcu_head *rcu)
@@ -659,6 +966,71 @@ int tlv_unset_params(struct tlv_param_table *tlv_param_table,
}
EXPORT_SYMBOL(tlv_unset_params);
+struct ipv6_txoptions *txoptions_from_opt(struct sock *sk,
+ struct tlv_param_table
+ *tlv_param_table,
+ struct ipv6_txoptions *opt,
+ int optname, char __user *optval,
+ unsigned int optlen)
+{
+ struct ipv6_opt_hdr *new = NULL;
+ struct net *net = sock_net(sk);
+ int retv;
+
+ /* remove any sticky options header with a zero option
+ * length, per RFC3542.
+ */
+ if (optlen == 0) {
+ optval = NULL;
+ } else if (!optval) {
+ return ERR_PTR(-EINVAL);
+ } else if (optlen < sizeof(struct ipv6_opt_hdr) ||
+ optlen & 0x7 || optlen > 8 * 255) {
+ return ERR_PTR(-EINVAL);
+ } else {
+ new = memdup_user(optval, optlen);
+ if (IS_ERR(new))
+ return (struct ipv6_txoptions *)new;
+ if (unlikely(ipv6_optlen(new) > optlen)) {
+ kfree(new);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ 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, tlv_param_table,
+ sk, optname, cap);
+ if (retv < 0) {
+ kfree(new);
+ return ERR_PTR(retv);
+ }
+
+ /* Check permissions and other validations on new
+ * TLVs
+ */
+ if (new) {
+ retv = ipv6_opt_validate_tlvs(net,
+ &ipv6_tlv_param_table,
+ new, optname, cap);
+ if (retv < 0) {
+ kfree(new);
+ return ERR_PTR(retv);
+ }
+ }
+ }
+
+ opt = ipv6_renew_options(sk, opt, optname, new);
+ kfree(new);
+
+ return opt;
+}
+EXPORT_SYMBOL(txoptions_from_opt);
+
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, },
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 40f21fe..dcc7d49 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -398,39 +398,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
case IPV6_DSTOPTS:
{
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;
+ opt = txoptions_from_opt(sk, &ipv6_tlv_param_table,
+ rcu_dereference_protected(np->opt,
+ lockdep_sock_is_held(sk)),
+ optname, optval, optlen);
- /* remove any sticky options header with a zero option
- * length, per RFC3542.
- */
- if (optlen == 0)
- optval = NULL;
- else if (!optval)
- goto e_inval;
- else if (optlen < sizeof(struct ipv6_opt_hdr) ||
- optlen & 0x7 || optlen > 8 * 255)
- goto e_inval;
- else {
- new = memdup_user(optval, optlen);
- if (IS_ERR(new)) {
- retv = PTR_ERR(new);
- break;
- }
- if (unlikely(ipv6_optlen(new) > optlen)) {
- kfree(new);
- goto e_inval;
- }
- }
-
- opt = rcu_dereference_protected(np->opt,
- lockdep_sock_is_held(sk));
- opt = ipv6_renew_options(sk, opt, optname, new);
- kfree(new);
if (IS_ERR(opt)) {
retv = PTR_ERR(opt);
break;
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
@ 2019-04-11 19:55 ` kbuild test robot
2019-04-11 20:36 ` kbuild test robot
2019-04-11 20:56 ` kbuild test robot
2 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2019-04-11 19:55 UTC (permalink / raw
To: Tom Herbert; +Cc: kbuild-all, davem, netdev, Tom Herbert
[-- Attachment #1: Type: text/plain, Size: 4096 bytes --]
Hi Tom,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Tom-Herbert/exthdrs-Create-exthdrs_options-c/20190412-024542
config: sparc64-allyesconfig (attached as .config)
compiler: sparc64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=sparc64
All error/warnings (new ones prefixed by >>):
net//ipv6/exthdrs_core.c: In function 'tlv_param_table_release':
>> net//ipv6/exthdrs_core.c:429:2: error: implicit declaration of function 'vfree'; did you mean 'kvfree'? [-Werror=implicit-function-declaration]
vfree(tpt);
^~~~~
kvfree
net//ipv6/exthdrs_core.c: In function '__tlv_set_proc':
>> net//ipv6/exthdrs_core.c:471:8: error: implicit declaration of function 'vmalloc'; did you mean 'kvmalloc'? [-Werror=implicit-function-declaration]
tpt = vmalloc(tlv_param_table_size(count));
^~~~~~~
kvmalloc
>> net//ipv6/exthdrs_core.c:471:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
tpt = vmalloc(tlv_param_table_size(count));
^
net//ipv6/exthdrs_core.c: In function '__tlv_unset_proc':
net//ipv6/exthdrs_core.c:507:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
tpt = vmalloc(tlv_param_table_size(old->count - 1));
^
net//ipv6/exthdrs_core.c: In function 'exthdrs_init':
net//ipv6/exthdrs_core.c:659:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
tpt = vmalloc(tsize);
^
cc1: some warnings being treated as errors
vim +429 net//ipv6/exthdrs_core.c
423
424 static void tlv_param_table_release(struct rcu_head *rcu)
425 {
426 struct tlv_param_table_data *tpt =
427 container_of(rcu, struct tlv_param_table_data, rcu);
428
> 429 vfree(tpt);
430 }
431
432 /* Default (unset) values for TLV parameters */
433 static const struct tlv_proc tlv_default_proc = {
434 .params.rx_class = 0,
435 };
436
437 static size_t tlv_param_table_size(unsigned char count)
438 {
439 return sizeof(struct tlv_param_table_data) +
440 (count * sizeof(struct tlv_proc));
441 }
442
443 static DEFINE_MUTEX(tlv_mutex);
444
445 /* mutex held */
446 static int __tlv_set_proc(struct tlv_param_table *tlv_param_table,
447 unsigned char type, const struct tlv_ops *ops,
448 const struct tlv_params *params)
449 {
450 struct tlv_param_table_data *tpt, *old;
451 struct tlv_proc *tproc;
452 unsigned char count, pos;
453
454 old = rcu_dereference_protected(tlv_param_table->data,
455 lockdep_is_held(&tlv_mutex));
456
457 if (old->entries[type]) {
458 /* Type is already set, modifying entry */
459 pos = old->entries[type];
460 count = old->count;
461
462 /* If ops is not provided, take them from existing proc */
463 if (!ops)
464 ops = &old->procs[pos].ops;
465 } else {
466 /* Type entry unset, need to create new entry */
467 pos = old->count;
468 count = pos + 1;
469 }
470
> 471 tpt = vmalloc(tlv_param_table_size(count));
472 if (!tpt)
473 return -ENOMEM;
474
475 memcpy(tpt, old, tlv_param_table_size(old->count));
476
477 tproc = &tpt->procs[pos];
478 tproc->params = *params;
479 tproc->ops = ops ? *ops : tlv_default_proc.ops;
480
481 tpt->entries[type] = pos;
482 tpt->count = count;
483
484 rcu_assign_pointer(tlv_param_table->data, tpt);
485
486 call_rcu(&old->rcu, tlv_param_table_release);
487
488 return 0;
489 }
490
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56180 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options
2019-04-09 19:09 ` [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options Tom Herbert
@ 2019-04-11 19:58 ` kbuild test robot
0 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2019-04-11 19:58 UTC (permalink / raw
To: Tom Herbert; +Cc: kbuild-all, davem, netdev, Tom Herbert
[-- Attachment #1: Type: text/plain, Size: 4872 bytes --]
Hi Tom,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Tom-Herbert/exthdrs-Create-exthdrs_options-c/20190412-024542
config: i386-randconfig-x017-201914 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
net/ipv6/exthdrs_core.c: In function '__ipv6_opt_validate_tlvs':
>> net/ipv6/exthdrs_core.c:513:18: error: 'struct net' has no member named 'ipv6'; did you mean 'ipv4'?
max_len = net->ipv6.sysctl.max_hbh_opts_len;
^~~~
ipv4
net/ipv6/exthdrs_core.c:514:18: error: 'struct net' has no member named 'ipv6'; did you mean 'ipv4'?
max_cnt = net->ipv6.sysctl.max_hbh_opts_cnt;
^~~~
ipv4
net/ipv6/exthdrs_core.c:518:18: error: 'struct net' has no member named 'ipv6'; did you mean 'ipv4'?
max_len = net->ipv6.sysctl.max_dst_opts_len;
^~~~
ipv4
net/ipv6/exthdrs_core.c:519:18: error: 'struct net' has no member named 'ipv6'; did you mean 'ipv4'?
max_cnt = net->ipv6.sysctl.max_dst_opts_cnt;
^~~~
ipv4
vim +513 net/ipv6/exthdrs_core.c
488
489 static int __ipv6_opt_validate_tlvs(struct net *net,
490 struct tlv_param_table *tlv_param_table,
491 struct ipv6_opt_hdr *opt,
492 unsigned int optname, bool deleting,
493 bool admin)
494 {
495 unsigned int max_len = 0, max_cnt = 0, cnt = 0;
496 unsigned char *tlv = (unsigned char *)opt;
497 bool deep_check, did_deep_check = false;
498 unsigned int opt_len, tlv_len, offset;
499 unsigned int padding = 0, numpad = 0;
500 unsigned char prev_tlv_order = 0;
501 struct tlv_tx_params *tptx;
502 int retc, ret = -EINVAL;
503 struct tlv_proc *tproc;
504 unsigned int class;
505
506 opt_len = ipv6_optlen(opt);
507 offset = sizeof(*opt);
508
509 class = optname_to_tlv_class(optname);
510
511 switch (optname) {
512 case IPV6_HOPOPTS:
> 513 max_len = net->ipv6.sysctl.max_hbh_opts_len;
514 max_cnt = net->ipv6.sysctl.max_hbh_opts_cnt;
515 break;
516 case IPV6_RTHDRDSTOPTS:
517 case IPV6_DSTOPTS:
518 max_len = net->ipv6.sysctl.max_dst_opts_len;
519 max_cnt = net->ipv6.sysctl.max_dst_opts_cnt;
520 break;
521 }
522
523 rcu_read_lock();
524
525 while (offset < opt_len) {
526 switch (tlv[offset]) {
527 case IPV6_TLV_PAD1:
528 tlv_len = 1;
529 padding++;
530 numpad++;
531 break;
532 case IPV6_TLV_PADN:
533 if (offset + 1 >= opt_len)
534 goto out;
535
536 tlv_len = tlv[offset + 1] + 2;
537
538 if (offset + tlv_len > opt_len)
539 goto out;
540
541 padding += tlv_len;
542 numpad++;
543 break;
544 default:
545 if (offset + 1 >= opt_len)
546 goto out;
547
548 tlv_len = tlv[offset + 1] + 2;
549
550 if (offset + tlv_len > opt_len)
551 goto out;
552
553 tproc = tlv_get_proc(tlv_param_table, tlv[offset]);
554 tptx = &tproc->params.t;
555
556 retc = __ipv6_opt_validate_single_tlv(net, &tlv[offset],
557 tptx, class,
558 &deep_check,
559 deleting, admin);
560 if (retc < 0) {
561 ret = retc;
562 goto out;
563 }
564
565 if (deep_check) {
566 /* Check for too many options */
567 if (++cnt > max_cnt) {
568 ret = -E2BIG;
569 goto out;
570 }
571
572 /* Check order */
573 if (tptx->preferred_order < prev_tlv_order)
574 goto out;
575
576 /* Check alignment */
577 if ((offset % (tptx->align_mult + 1)) !=
578 tptx->align_off)
579 goto out;
580
581 /* Check for right amount of padding */
582 if (numpad > 1 || padding > tptx->align_mult)
583 goto out;
584
585 prev_tlv_order = tptx->preferred_order;
586 }
587
588 padding = 0;
589 numpad = 0;
590 did_deep_check = true;
591 }
592 offset += tlv_len;
593 }
594
595 /* If we did at least one deep check apply length limit */
596 if (did_deep_check && opt_len > max_len) {
597 ret = -EMSGSIZE;
598 goto out;
599 }
600
601 /* All good */
602 ret = 0;
603 out:
604 rcu_read_unlock();
605
606 return ret;
607 }
608
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 31140 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
2019-04-11 19:55 ` kbuild test robot
@ 2019-04-11 20:36 ` kbuild test robot
2019-04-11 20:56 ` kbuild test robot
2 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2019-04-11 20:36 UTC (permalink / raw
To: Tom Herbert; +Cc: kbuild-all, davem, netdev, Tom Herbert
[-- Attachment #1: Type: text/plain, Size: 1592 bytes --]
Hi Tom,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Tom-Herbert/exthdrs-Create-exthdrs_options-c/20190412-024542
config: i386-randconfig-l3-04110213 (attached as .config)
compiler: gcc-5 (Debian 5.5.0-3) 5.4.1 20171010
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
>> net/ipv6/exthdrs_options.c:182:20: error: 'ipv6_dest_hao' undeclared here (not in a function)
.proc.ops.func = ipv6_dest_hao,
^
vim +/ipv6_dest_hao +182 net/ipv6/exthdrs_options.c
177
178 static const struct tlv_proc_init tlv_init_params[] __initconst = {
179 {
180 .type = IPV6_TLV_HAO,
181
> 182 .proc.ops.func = ipv6_dest_hao,
183 .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_DSTOPT,
184 },
185 {
186 .type = IPV6_TLV_ROUTERALERT,
187
188 .proc.ops.func = ipv6_hop_ra,
189 .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
190 },
191 {
192 .type = IPV6_TLV_JUMBO,
193
194 .proc.ops.func = ipv6_hop_jumbo,
195 .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
196 },
197 {
198 .type = IPV6_TLV_CALIPSO,
199
200 .proc.ops.func = ipv6_hop_calipso,
201 .proc.params.rx_class = IPV6_TLV_CLASS_FLAG_HOPOPT,
202 },
203 };
204
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 27192 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
2019-04-11 19:55 ` kbuild test robot
2019-04-11 20:36 ` kbuild test robot
@ 2019-04-11 20:56 ` kbuild test robot
2 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2019-04-11 20:56 UTC (permalink / raw
To: Tom Herbert; +Cc: kbuild-all, davem, netdev, Tom Herbert
Hi Tom,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on net-next/master]
url: https://github.com/0day-ci/linux/commits/Tom-Herbert/exthdrs-Create-exthdrs_options-c/20190412-024542
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
sparse warnings: (new ones prefixed by >>)
net/ipv6/exthdrs_options.c:205:30: sparse: symbol 'ipv6_tlv_param_table' redeclared with different type (originally declared at include/net/ipv6.h:418) - different address spaces
>> net/ipv6/exthdrs_options.c:209:30: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct tlv_param_table *tlv_param_table @@ got struct tlv_param_struct tlv_param_table *tlv_param_table @@
net/ipv6/exthdrs_options.c:209:30: expected struct tlv_param_table *tlv_param_table
net/ipv6/exthdrs_options.c:209:30: got struct tlv_param_table [noderef] <asn:4>*<noident>
net/ipv6/exthdrs_options.c:216:23: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct tlv_param_table *tlv_param_table @@ got struct tlv_param_struct tlv_param_table *tlv_param_table @@
net/ipv6/exthdrs_options.c:216:23: expected struct tlv_param_table *tlv_param_table
net/ipv6/exthdrs_options.c:216:23: got struct tlv_param_table [noderef] <asn:4>*<noident>
vim +209 net/ipv6/exthdrs_options.c
204
> 205 struct tlv_param_table __rcu ipv6_tlv_param_table;
206
207 static int __init ipv6_exthdrs_init(void)
208 {
> 209 return exthdrs_init(&ipv6_tlv_param_table, tlv_init_params,
210 ARRAY_SIZE(tlv_init_params));
211 }
212 module_init(ipv6_exthdrs_init);
213
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2019-04-11 20:59 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-04-09 19:09 [PATCH v2 net-next 0/6] exthdrs: Make ext. headers & options useful - Part I Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 1/6] exthdrs: Create exthdrs_options.c Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 2/6] exthdrs: Move generic EH functions to exthdrs_core.c Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 3/6] exthdrs: Registration of TLV handlers and parameters Tom Herbert
2019-04-11 19:55 ` kbuild test robot
2019-04-11 20:36 ` kbuild test robot
2019-04-11 20:56 ` kbuild test robot
2019-04-09 19:09 ` [PATCH v2 net-next 4/6] exthdrs: Add TX parameters Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 5/6] ip6tlvs: Add netlink interface Tom Herbert
2019-04-09 19:09 ` [PATCH v2 net-next 6/6] ip6tlvs: Validation of TX Destination and Hop-by-Hop options Tom Herbert
2019-04-11 19:58 ` kbuild test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.