From patchwork Mon Jan 28 18:22:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 1032145 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="M7uudU2a"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43pJ0j1wwjz9sDB for ; Tue, 29 Jan 2019 05:23:33 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727446AbfA1SXc (ORCPT ); Mon, 28 Jan 2019 13:23:32 -0500 Received: from mail-pg1-f195.google.com ([209.85.215.195]:36917 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727052AbfA1SXa (ORCPT ); Mon, 28 Jan 2019 13:23:30 -0500 Received: by mail-pg1-f195.google.com with SMTP id c25so7563786pgb.4 for ; Mon, 28 Jan 2019 10:23:29 -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=N3Lc1/34Mf9+8T2sGNqK/yyCsDJ6BF1Elxn9Y+DHga0=; b=M7uudU2awuksW2oixqPKuJe2O8jRzYOIk5Eac+7D3Ksp1ziMAPdMa8d7qskbnGCDVG q1TtcPURGd77KUF/EbmRYBErM6NHzr99LKUMSg5I5x4axo6TjvzuCMMnvPNHcoEnEbFT Y+EsxbeO3RzSlC43n095skdpGeqOr71Sml/AgwGGmOjDy03wxoAxfwbH6xrpPN11Isof 9DVz3pUF1NMTzWykai0D6maNtpqamXOcdj0V9teH6OtjFSMlTluhJhb/SCVegcdRA3l8 m4ajQNqiLG2YxeML3utB9ISHKA1+mUP8gWAG7DMntfVLsNZngjvIxO2ttTfLToJJIyra eBVQ== 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=N3Lc1/34Mf9+8T2sGNqK/yyCsDJ6BF1Elxn9Y+DHga0=; b=bjk7h+Jj2wKHM0MoAQ4VStBSx9sFOAmxgoAs38s1bHl0dSqwz3uFiO1BRIad8tCvPU cWbgOdYXXQUPlUowXL8PfCLGoHjbTrYG/PwSswSYN4w5SymsRLvdkn5xH/O902COb3ZW v2PU6TdNgurJVYoy5zwKiKBnRCjdj3rfzv3NYexdM31x4I1C2f5B6D6NJh3mb2XmGEKQ Tf5uzxVMN9mMhd0SusTd3ctUR5Aw86V523rl/E9yqBHdrZHFmcAZ6tEfYNDgu5O7bMN+ fIE1bZX+KRgjaCEghBrQpJFocTmNMX+dWF90EGCJSIz5SiFoMWFL/UY5nxCYWNr6SG3S HMjA== X-Gm-Message-State: AJcUukfmEssmdgFyG3kO6o1OPp9Q4SWXzP+BJMbX54VbN5nT64PYVY9F gqvEkp1d422Ka9K52IJ1srBdWajpft0= X-Google-Smtp-Source: ALg8bN5DvnUHkaJ42AZIX3yQjPllr0fFD4c9zHAwi8Rf1F+9fNmjuoS1ZAjwAL+8g4yRopElBaIQ9A== X-Received: by 2002:a63:dc0c:: with SMTP id s12mr21013317pgg.398.1548699809225; Mon, 28 Jan 2019 10:23:29 -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 n78sm61777238pfk.19.2019.01.28.10.23.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 28 Jan 2019 10:23:28 -0800 (PST) From: Tom Herbert X-Google-Original-From: Tom Herbert To: davem@davemloft.net, netdev@vger.kernel.org Cc: Tom Herbert Subject: [PATCH v2 net-next 3/5] ip6tlvs: Add netlink interface Date: Mon, 28 Jan 2019 10:22:53 -0800 Message-Id: <1548699775-3015-4-git-send-email-tom@quantonium.net> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1548699775-3015-1-git-send-email-tom@quantonium.net> References: <1548699775-3015-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 | 300 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 331 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 38e8e63..a54cf96 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -297,6 +297,38 @@ struct in6_flowlabel_req { * MRT6_MAX */ +/* NETLINK_GENERIC related info for IPv6 TLVs */ + +#define IPV6_TLV_GENL_NAME "ipv6-tlv" +#define IPV6_TLV_GENL_VERSION 0x1 + +enum { + IPV6_TLV_ATTR_UNSPEC, + IPV6_TLV_ATTR_TYPE, /* u8, > 1 */ + IPV6_TLV_ATTR_ORDER, /* u8 */ + IPV6_TLV_ATTR_ADMIN_PERM, /* u8, perm value */ + IPV6_TLV_ATTR_USER_PERM, /* u8, perm value */ + IPV6_TLV_ATTR_CLASS, /* u8, 3 bit flags */ + IPV6_TLV_ATTR_ALIGN_MULT, /* u8, 1 to 16 */ + IPV6_TLV_ATTR_ALIGN_OFF, /* u8, 0 to 15 */ + IPV6_TLV_ATTR_MIN_DATA_LEN, /* u8 (option data length) */ + IPV6_TLV_ATTR_MAX_DATA_LEN, /* u8 (option data length) */ + IPV6_TLV_ATTR_DATA_LEN_MULT, /* u8, 1 to 16 */ + IPV6_TLV_ATTR_DATA_LEN_OFF, /* u8, 0 to 15 */ + + __IPV6_TLV_ATTR_MAX, +}; + +#define IPV6_TLV_ATTR_MAX (__IPV6_TLV_ATTR_MAX - 1) + +enum { + IPV6_TLV_CMD_SET, + IPV6_TLV_CMD_UNSET, + IPV6_TLV_CMD_GET, + + __IPV6_TLV_CMD_MAX, +}; + /* TLV permissions values */ enum { IPV6_TLV_PERM_NONE, diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c index 397d9b3..a7811e0 100644 --- a/net/ipv6/exthdrs_options.c +++ b/net/ipv6/exthdrs_options.c @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #if IS_ENABLED(CONFIG_IPV6_MIP6) #include #endif +#include #include /* Parsing tlv encoded headers. @@ -629,12 +631,298 @@ static const struct tlv_init_params tlv_init_params[] __initconst = { } }; +static struct genl_family tlv_nl_family; + +static const struct nla_policy tlv_nl_policy[IPV6_TLV_ATTR_MAX + 1] = { + [IPV6_TLV_ATTR_TYPE] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ORDER] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ADMIN_PERM] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_USER_PERM] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_CLASS] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ALIGN_MULT] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_ALIGN_OFF] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_MIN_DATA_LEN] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_MAX_DATA_LEN] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_DATA_LEN_OFF] = { .type = NLA_U8, }, + [IPV6_TLV_ATTR_DATA_LEN_MULT] = { .type = NLA_U8, }, +}; + +static int tlv_nl_cmd_set(struct sk_buff *skb, struct genl_info *info) +{ + struct tlv_tx_param new_tx, *tptx; + int retv = -EINVAL, i; + u8 tlv_type, v; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + rcu_read_lock(); + + new_tx = *tlv_deref_tx_params(tlv_type); + + if (info->attrs[IPV6_TLV_ATTR_ORDER]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ORDER]); + if (v) { + struct tlv_tx_param *tptx; + + for (i = 2; i < 256; i++) { + /* Preferred orders must be unique */ + tptx = tlv_deref_tx_params(i); + if (tptx->preferred_order == v && + i != tlv_type) { + retv = -EALREADY; + goto out; + } + } + new_tx.preferred_order = v; + } + } + + if (!new_tx.preferred_order) { + unsigned long check_map[BITS_TO_LONGS(255)]; + int pos; + + /* Preferred order not specified, automatically set one. + * This is chosen to be the first value after the greatest + * order in use. + */ + memset(check_map, 0, sizeof(check_map)); + + for (i = 2; i < 256; i++) { + unsigned int order; + + tptx = tlv_deref_tx_params(i); + order = tptx->preferred_order; + + if (!order) + continue; + + WARN_ON(test_bit(255 - order, check_map)); + set_bit(255 - order, check_map); + } + + pos = find_first_bit(check_map, 255); + if (pos) + new_tx.preferred_order = 255 - (pos - 1); + else + new_tx.preferred_order = 255 - + find_first_zero_bit(check_map, sizeof(check_map)); + } + + if (info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ADMIN_PERM]); + if (v > IPV6_TLV_PERM_MAX) + goto out; + new_tx.admin_perm = v; + } + + if (info->attrs[IPV6_TLV_ATTR_USER_PERM]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_USER_PERM]); + if (v > IPV6_TLV_PERM_MAX) + goto out; + new_tx.user_perm = v; + } + + if (info->attrs[IPV6_TLV_ATTR_CLASS]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_CLASS]); + if (v > IPV6_TLV_CLASS_MAX) + goto out; + new_tx.class = v; + } + + if (info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_MULT]); + if (v > 16 || v < 1) + goto out; + new_tx.align_mult = v - 1; + } + + if (info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_ALIGN_OFF]); + if (v > 15) + goto out; + new_tx.align_off = v; + } + + if (info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]) + new_tx.max_data_len = + nla_get_u8(info->attrs[IPV6_TLV_ATTR_MAX_DATA_LEN]); + + if (info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN]) + new_tx.min_data_len = + nla_get_u8(info->attrs[IPV6_TLV_ATTR_MIN_DATA_LEN]); + + if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_MULT]); + if (v > 16 || v < 1) + goto out; + new_tx.data_len_mult = v - 1; + } + + if (info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]) { + v = nla_get_u8(info->attrs[IPV6_TLV_ATTR_DATA_LEN_OFF]); + if (v > 15) + goto out; + new_tx.data_len_off = v; + } + + retv = tlv_set_tx_param(tlv_type, &new_tx); + +out: + rcu_read_unlock(); + return retv; +} + +static int tlv_nl_cmd_unset(struct sk_buff *skb, struct genl_info *info) +{ + unsigned int tlv_type; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + return tlv_unset_tx_param(tlv_type); +} + +static int tlv_fill_info(int tlv_type, struct sk_buff *msg, bool admin) +{ + struct tlv_tx_param *tptx; + int ret = 0; + + rcu_read_lock(); + + tptx = tlv_deref_tx_params(tlv_type); + + if (nla_put_u8(msg, IPV6_TLV_ATTR_TYPE, tlv_type) || + nla_put_u8(msg, IPV6_TLV_ATTR_ORDER, tptx->preferred_order) || + nla_put_u8(msg, IPV6_TLV_ATTR_USER_PERM, tptx->user_perm) || + (admin && nla_put_u8(msg, IPV6_TLV_ATTR_ADMIN_PERM, + tptx->admin_perm)) || + nla_put_u8(msg, IPV6_TLV_ATTR_CLASS, tptx->class) || + nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_MULT, tptx->align_mult + 1) || + nla_put_u8(msg, IPV6_TLV_ATTR_ALIGN_OFF, tptx->align_off) || + nla_put_u8(msg, IPV6_TLV_ATTR_MIN_DATA_LEN, tptx->min_data_len) || + nla_put_u8(msg, IPV6_TLV_ATTR_MAX_DATA_LEN, tptx->max_data_len) || + nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_MULT, + tptx->data_len_mult + 1) || + nla_put_u8(msg, IPV6_TLV_ATTR_DATA_LEN_OFF, tptx->data_len_off)) + ret = -1; + + rcu_read_unlock(); + + return ret; +} + +static int tlv_dump_info(int tlv_type, u32 portid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd, bool admin) +{ + void *hdr; + + hdr = genlmsg_put(skb, portid, seq, &tlv_nl_family, flags, cmd); + if (!hdr) + return -ENOMEM; + + if (tlv_fill_info(tlv_type, skb, admin) < 0) { + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; + } + + genlmsg_end(skb, hdr); + + return 0; +} + +static int tlv_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + int ret, tlv_type; + + if (!info->attrs[IPV6_TLV_ATTR_TYPE]) + return -EINVAL; + + tlv_type = nla_get_u8(info->attrs[IPV6_TLV_ATTR_TYPE]); + if (tlv_type < 2) + return -EINVAL; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + ret = tlv_dump_info(tlv_type, info->snd_portid, info->snd_seq, 0, msg, + info->genlhdr->cmd, + netlink_capable(skb, CAP_NET_ADMIN)); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_reply(msg, info); +} + +static int tlv_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0, ret, i; + + for (i = 2; i < 256; i++) { + if (idx++ < cb->args[0]) + continue; + ret = tlv_dump_info(i, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + skb, IPV6_TLV_CMD_GET, + netlink_capable(cb->skb, CAP_NET_ADMIN)); + if (ret) + break; + } + + cb->args[0] = idx; + return skb->len; +} + +static const struct genl_ops tlv_nl_ops[] = { +{ + .cmd = IPV6_TLV_CMD_SET, + .doit = tlv_nl_cmd_set, + .policy = tlv_nl_policy, + .flags = GENL_ADMIN_PERM, +}, +{ + .cmd = IPV6_TLV_CMD_UNSET, + .doit = tlv_nl_cmd_unset, + .policy = tlv_nl_policy, + .flags = GENL_ADMIN_PERM, +}, +{ + .cmd = IPV6_TLV_CMD_GET, + .doit = tlv_nl_cmd_get, + .dumpit = tlv_nl_dump, + .policy = tlv_nl_policy, +}, +}; + +static struct genl_family tlv_nl_family __ro_after_init = { + .hdrsize = 0, + .name = IPV6_TLV_GENL_NAME, + .version = IPV6_TLV_GENL_VERSION, + .maxattr = IPV6_TLV_ATTR_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = tlv_nl_ops, + .n_ops = ARRAY_SIZE(tlv_nl_ops), +}; + static int __init exthdrs_init(void) { unsigned long check_map[BITS_TO_LONGS(256)]; struct tlv_param_table *tpt; size_t tsize; - int i; + int i, ret; memset(check_map, 0, sizeof(check_map)); @@ -676,11 +964,21 @@ static int __init exthdrs_init(void) RCU_INIT_POINTER(tlv_param_table, tpt); + ret = genl_register_family(&tlv_nl_family); + if (ret < 0) + goto fail_genl_register; + return 0; + +fail_genl_register: + __tlv_destroy_param_table(); + + return ret; } module_init(exthdrs_init); static void __exit exthdrs_fini(void) { + genl_unregister_family(&tlv_nl_family); } module_exit(exthdrs_fini);