From patchwork Wed Jan 23 05:31:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 1029665 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=herbertland.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=herbertland-com.20150623.gappssmtp.com header.i=@herbertland-com.20150623.gappssmtp.com header.b="qcMphc9S"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43kv7L2Dd9z9s3q for ; Wed, 23 Jan 2019 16:32:30 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726310AbfAWFc3 (ORCPT ); Wed, 23 Jan 2019 00:32:29 -0500 Received: from mail-pl1-f195.google.com ([209.85.214.195]:41780 "EHLO mail-pl1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725899AbfAWFc2 (ORCPT ); Wed, 23 Jan 2019 00:32:28 -0500 Received: by mail-pl1-f195.google.com with SMTP id u6so553959plm.8 for ; Tue, 22 Jan 2019 21:32:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=herbertland-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Z8nthDeszs7Mo9egJHssdRc/4YXgvc6e2mhFt0Io43s=; b=qcMphc9SHk/Nra5MqkTwwkFjabqiNGoAvLe5XQezMr4ijfCXCoIOYMS3vNQg/iXmFd 9OgDihGyX/8RZldIKhOt4SwHHhX01XmnTwcdywYnXhhQcoaDVt2mmfJs0VM5WyI6BXmr RlZCxVkMdZzj01VLFbTV2e7BkLdKkJ2gTO5JGgSIsH41qp467Q8afdfx2dWQBe3qfxzW tdbxpJ7TffRspnSfkvfu1zDu5jDDF1u0ZBK81YTuSRs7Oi3z9hUv08kCJnSwYMjHNWO4 bsuOYBGDSA+a6AX8HTojxh/RsnEf6heTjzHGO5Pqw8DnKayzjdbmUl0fLdxAgnooqFKn 9/pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Z8nthDeszs7Mo9egJHssdRc/4YXgvc6e2mhFt0Io43s=; b=I+uR37/oUkdJ6RsBeXu23pAc1ufSJeUrQ2ZXI0w+WleoHxQ+Bv3EnfCguC54QbEWgy v+m/dKhX2IdljYJYIXmsYpSY7+gt6Qx7QbWoPJpX1Tplnvb9F4iC9BIJL1iOfS95R2Hu 20GC1zQQeiHegko3ghjZd7GeSD6AEc6r5vlzXWQS7I6DHew4hE4eWDyp8FB5lJFJignV gp6l5HnY08Cjt6c9MtkfJcu1oLnwKu7WO0WFDT7zNM3uplzCDJP387+VQVl6tCBpw/Ek UzW5I6lFpsA+b/HNk8jesItZNI7gXCdIcXE7ROoJ/PJAxvJcjiU2/bRPOwqoyI7hsMxw OIiw== X-Gm-Message-State: AJcUukfLKZ+upcE5bChdPeWrhbc8BnRjnj7OMcabc9NoO42QBarXcF0n M2TrPCLesAzr2DyZQx8PrFUeJp9MIcA= X-Google-Smtp-Source: ALg8bN7iK/bbdd2ckTgxA6GU8W4GvytF+xsZfiQkBDh9Vrq1IRLot8BX+w1HdKO3vWs6zOYR8JmL9Q== X-Received: by 2002:a17:902:ac8f:: with SMTP id h15mr841540plr.245.1548221547873; Tue, 22 Jan 2019 21:32:27 -0800 (PST) Received: from localhost.localdomain (c-73-223-249-119.hsd1.ca.comcast.net. [73.223.249.119]) by smtp.gmail.com with ESMTPSA id o66sm31700948pgo.75.2019.01.22.21.32.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 22 Jan 2019 21:32:27 -0800 (PST) From: Tom Herbert X-Google-Original-From: Tom Herbert To: davem@davemloft.net, netdev@vger.kernel.org Cc: Tom Herbert Subject: [PATCH net-next 3/5] ip6tlvs: Add netlink interface Date: Tue, 22 Jan 2019 21:31:21 -0800 Message-Id: <1548221483-3085-4-git-send-email-tom@quantonium.net> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1548221483-3085-1-git-send-email-tom@quantonium.net> References: <1548221483-3085-1-git-send-email-tom@quantonium.net> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add a netlink interface to manage the TX TLV parameters. Managed parameters include those for validating and sending TLVs being sent such as alignment, TLV ordering, length limits, etc. --- include/uapi/linux/in6.h | 32 +++++ net/ipv6/exthdrs_options.c | 292 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 38e8e63..a54cf96 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -297,6 +297,38 @@ struct in6_flowlabel_req { * MRT6_MAX */ +/* NETLINK_GENERIC related info for IPv6 TLVs */ + +#define IPV6_TLV_GENL_NAME "ipv6-tlv" +#define IPV6_TLV_GENL_VERSION 0x1 + +enum { + IPV6_TLV_ATTR_UNSPEC, + IPV6_TLV_ATTR_TYPE, /* u8, > 1 */ + IPV6_TLV_ATTR_ORDER, /* u8 */ + IPV6_TLV_ATTR_ADMIN_PERM, /* u8, perm value */ + IPV6_TLV_ATTR_USER_PERM, /* u8, perm value */ + IPV6_TLV_ATTR_CLASS, /* u8, 3 bit flags */ + IPV6_TLV_ATTR_ALIGN_MULT, /* u8, 1 to 16 */ + IPV6_TLV_ATTR_ALIGN_OFF, /* u8, 0 to 15 */ + IPV6_TLV_ATTR_MIN_DATA_LEN, /* u8 (option data length) */ + IPV6_TLV_ATTR_MAX_DATA_LEN, /* u8 (option data length) */ + IPV6_TLV_ATTR_DATA_LEN_MULT, /* u8, 1 to 16 */ + IPV6_TLV_ATTR_DATA_LEN_OFF, /* u8, 0 to 15 */ + + __IPV6_TLV_ATTR_MAX, +}; + +#define IPV6_TLV_ATTR_MAX (__IPV6_TLV_ATTR_MAX - 1) + +enum { + IPV6_TLV_CMD_SET, + IPV6_TLV_CMD_UNSET, + IPV6_TLV_CMD_GET, + + __IPV6_TLV_CMD_MAX, +}; + /* TLV permissions values */ enum { IPV6_TLV_PERM_NONE, diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c index a1b7a2e..da9e257 100644 --- a/net/ipv6/exthdrs_options.c +++ b/net/ipv6/exthdrs_options.c @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #if IS_ENABLED(CONFIG_IPV6_MIP6) #include #endif +#include #include /* Parsing tlv encoded headers. @@ -560,6 +562,291 @@ static const struct tlv_init_params tlv_init_params[] __initconst = { } }; +static struct genl_family tlv_nl_family; + +static const struct nla_policy tlv_nl_policy[IPV6_TLV_ATTR_MAX + 1] = { + [IPV6_TLV_ATTR_TYPE] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ORDER] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ADMIN_PERM] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_USER_PERM] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_CLASS] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ALIGN_MULT] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ALIGN_OFF] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_MIN_DATA_LEN] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_MAX_DATA_LEN] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_DATA_LEN_OFF] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_DATA_LEN_MULT] = { .type = NLA_U8, }, +}; + +static int tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info) +{ + struct tlv_tx_param new_tx; + int retv = -EINVAL, i; + struct tlv_param *tp; + u8 tlv_type, v; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + rcu_read_lock(); + + new_tx = *tlv_deref_tx_params(tlv_type); + + if (info->attrs[IPV6_TLV_ATTR_ORDER]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ORDER]); + if (v) { + for (i = 2; i < 256; i++) { + /* Preferred orders must be unique */ + tp = rcu_dereference(tlv_param_table[i]); + if (tp->tx_params.preferred_order == v && + i != tlv_type) { + retv = -EALREADY; + goto out; + } + } + new_tx.preferred_order = v; + } + } + + if (!new_tx.preferred_order) { + unsigned long check_map[BITS_TO_LONGS(255)]; + int pos; + + /* Preferred order not specified, automatically set one. + * This is chosen to be the first value after the greatest + * order in use. + */ + memset(check_map, 0, sizeof(check_map)); + + for (i = 2; i < 256; i++) { + unsigned int order; + + tp = rcu_dereference(tlv_param_table[i]); + order = tp->tx_params.preferred_order; + + if (!order) + continue; + + WARN_ON(test_bit(255 - order, check_map)); + set_bit(255 - order, check_map); + } + + pos = find_first_bit(check_map, 255); + if (pos) + new_tx.preferred_order = 255 - (pos - 1); + else + new_tx.preferred_order = 255 - + find_first_zero_bit(check_map, sizeof(check_map)); + } + + if (info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]); + if (v > IPV6_TLV_PERM_MAX) + goto out; + new_tx.admin_perm = v; + } + + if (info->attrs[IPV6_TLV_ATTR_USER_PERM]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_USER_PERM]); + if (v > IPV6_TLV_PERM_MAX) + goto out; + new_tx.user_perm = v; + } + + if (info->attrs[IPV6_TLV_ATTR_CLASS]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_CLASS]); + if (v > IPV6_TLV_CLASS_MAX) + goto out; + new_tx.class = v; + } + + if (info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]); + if (v > 16 || v < 1) + goto out; + new_tx.align_mult = v - 1; + } + + if (info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]); + if (v > 15) + goto out; + new_tx.align_off = v; + } + + if (info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]) + new_tx.max_data_len = + nla_get_u8(info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]); + + if (info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN]) + new_tx.min_data_len = + nla_get_u8(info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN]); + + if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]); + if (v > 16 || v < 1) + goto out; + new_tx.data_len_mult = v - 1; + } + + if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]); + if (v > 15) + goto out; + new_tx.data_len_off = v; + } + + retv = tlv_set_tx_param(tlv_type, &new_tx); + +out: + rcu_read_unlock(); + return retv; +} + +static int tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info) +{ + unsigned int tlv_type; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + return tlv_unset_tx_param(tlv_type); +} + +static int tlv_fill_info(int tlv_type, struct sk_buff *msg, bool admin) +{ + struct tlv_tx_param *tptx; + int ret = 0; + + rcu_read_lock(); + + tptx = tlv_deref_tx_params(tlv_type); + + if (nla_put_u8(msg, IPV6_TLV_ATTR_TYPE, tlv_type) || + nla_put_u8(msg, IPV6_TLV_ATTR_ORDER, tptx->preferred_order) || + nla_put_u8(msg, IPV6_TLV_ATTR_USER_PERM, tptx->user_perm) || + (admin && nla_put_u8(msg, IPV6_TLV_ATTR_ADMIN_PERM, + tptx->admin_perm)) || + nla_put_u8(msg, IPV6_TLV_ATTR_CLASS, tptx->class) || + nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_MULT, tptx->align_mult + 1) || + nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_OFF, tptx->align_off) || + nla_put_u8(msg, IPV6_TLV_ATTR_MIN_DATA_LEN, tptx->min_data_len) || + nla_put_u8(msg, IPV6_TLV_ATTR_MAX_DATA_LEN, tptx->max_data_len) || + nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_MULT, + tptx->data_len_mult + 1) || + nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_OFF, tptx->data_len_off)) + ret = -1; + + rcu_read_unlock(); + + return ret; +} + +static int tlv_dump_info(int tlv_type, u32 portid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd, bool admin) +{ + void *hdr; + + hdr = genlmsg_put(skb, portid, seq, &tlv_nl_family, flags, cmd); + if (!hdr) + return -ENOMEM; + + if (tlv_fill_info(tlv_type, skb, admin) < 0) { + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; + } + + genlmsg_end(skb, hdr); + + return 0; +} + +static int tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + int ret, tlv_type; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + ret = tlv_dump_info(tlv_type, info->snd_portid, info->snd_seq, 0, msg, + info->genlhdr->cmd, + netlink_capable(skb, CAP_NET_ADMIN)); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_reply(msg, info); +} + +static int tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0, ret, i; + + for (i = 2; i < 256; i++) { + if (idx++ < cb->args[0]) + continue; + ret = tlv_dump_info(i, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + skb, IPV6_TLV_CMD_GET, + netlink_capable(cb->skb, CAP_NET_ADMIN)); + if (ret) + break; + } + + cb->args[0] = idx; + return skb->len; +} + +static const struct genl_ops tlv_nl_ops[] = { +{ + .cmd = IPV6_TLV_CMD_SET, + .doit = tlv_nl_cmd_set, + .policy = tlv_nl_policy, + .flags = GENL_ADMIN_PERM, +}, +{ + .cmd = IPV6_TLV_CMD_UNSET, + .doit = tlv_nl_cmd_unset, + .policy = tlv_nl_policy, + .flags = GENL_ADMIN_PERM, +}, +{ + .cmd = IPV6_TLV_CMD_GET, + .doit = tlv_nl_cmd_get, + .dumpit = tlv_nl_dump, + .policy = tlv_nl_policy, +}, +}; + +static struct genl_family tlv_nl_family __ro_after_init = { + .hdrsize = 0, + .name = IPV6_TLV_GENL_NAME, + .version = IPV6_TLV_GENL_VERSION, + .maxattr = IPV6_TLV_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = tlv_nl_ops, + .n_ops = ARRAY_SIZE(tlv_nl_ops), +}; + static int __init exthdrs_init(void) { unsigned long check_map[BITS_TO_LONGS(256)]; @@ -596,6 +883,10 @@ static int __init exthdrs_init(void) goto fail; } + ret = genl_register_family(&tlv_nl_family); + if (ret < 0) + goto fail; + return 0; fail: @@ -612,5 +903,6 @@ module_init(exthdrs_init); static void __exit exthdrs_fini(void) { + genl_unregister_family(&tlv_nl_family); } module_exit(exthdrs_fini);