get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1148475/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1148475,
    "url": "http://patchwork.ozlabs.org/api/patches/1148475/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/patch/1565992424-22379-8-git-send-email-tom@herbertland.com/",
    "project": {
        "id": 46,
        "url": "http://patchwork.ozlabs.org/api/projects/46/?format=api",
        "name": "Intel Wired Ethernet development",
        "link_name": "intel-wired-lan",
        "list_id": "intel-wired-lan.osuosl.org",
        "list_email": "intel-wired-lan@osuosl.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1565992424-22379-8-git-send-email-tom@herbertland.com>",
    "list_archive_url": null,
    "date": "2019-08-16T21:53:44",
    "name": "[v2,net-next,7/7] ip6tlvs: Validation of TX Destination and Hop-by-Hop options",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "2a8c1294a4ccc892f6243ac361f63fe769f8d6d5",
    "submitter": {
        "id": 65986,
        "url": "http://patchwork.ozlabs.org/api/people/65986/?format=api",
        "name": "Tom Herbert",
        "email": "tom@herbertland.com"
    },
    "delegate": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/users/68/?format=api",
        "username": "jtkirshe",
        "first_name": "Jeff",
        "last_name": "Kirsher",
        "email": "jeffrey.t.kirsher@intel.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/intel-wired-lan/patch/1565992424-22379-8-git-send-email-tom@herbertland.com/mbox/",
    "series": [
        {
            "id": 125682,
            "url": "http://patchwork.ozlabs.org/api/series/125682/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/list/?series=125682",
            "date": "2019-08-16T21:53:37",
            "name": "ipv6: Extension header infrastructure",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/125682/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1148475/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1148475/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<intel-wired-lan-bounces@osuosl.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "Intel-wired-lan@lists.osuosl.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "Intel-wired-lan@lists.osuosl.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=osuosl.org\n\t(client-ip=140.211.166.138; helo=whitealder.osuosl.org;\n\tenvelope-from=intel-wired-lan-bounces@osuosl.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org; dmarc=none (p=none dis=none)\n\theader.from=herbertland.com",
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=herbertland-com.20150623.gappssmtp.com\n\theader.i=@herbertland-com.20150623.gappssmtp.com\n\theader.b=\"FU/tCHfH\"; dkim-atps=neutral"
        ],
        "Received": [
            "from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 469HCw63Y3z9sML\n\tfor <incoming@patchwork.ozlabs.org>;\n\tSat, 17 Aug 2019 07:54:36 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby whitealder.osuosl.org (Postfix) with ESMTP id 2AAC7878BD;\n\tFri, 16 Aug 2019 21:54:35 +0000 (UTC)",
            "from whitealder.osuosl.org ([127.0.0.1])\n\tby localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id tWi8VsbNkq9i; Fri, 16 Aug 2019 21:54:31 +0000 (UTC)",
            "from ash.osuosl.org (ash.osuosl.org [140.211.166.34])\n\tby whitealder.osuosl.org (Postfix) with ESMTP id 083E6865AB;\n\tFri, 16 Aug 2019 21:54:31 +0000 (UTC)",
            "from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136])\n\tby ash.osuosl.org (Postfix) with ESMTP id B0CBE1BF9C1\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tFri, 16 Aug 2019 21:54:24 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n\tby silver.osuosl.org (Postfix) with ESMTP id AA84A2050A\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tFri, 16 Aug 2019 21:54:24 +0000 (UTC)",
            "from silver.osuosl.org ([127.0.0.1])\n\tby localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id mZuEtm7PNTGB for <Intel-wired-lan@lists.osuosl.org>;\n\tFri, 16 Aug 2019 21:54:21 +0000 (UTC)",
            "from mail-pl1-f193.google.com (mail-pl1-f193.google.com\n\t[209.85.214.193])\n\tby silver.osuosl.org (Postfix) with ESMTPS id D170D20798\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tFri, 16 Aug 2019 21:54:18 +0000 (UTC)",
            "by mail-pl1-f193.google.com with SMTP id a93so2970366pla.7\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tFri, 16 Aug 2019 14:54:18 -0700 (PDT)",
            "from localhost.localdomain (c-73-202-182-113.hsd1.ca.comcast.net.\n\t[73.202.182.113]) by smtp.gmail.com with ESMTPSA id\n\tr12sm7151355pgb.73.2019.08.16.14.54.16\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tFri, 16 Aug 2019 14:54:17 -0700 (PDT)"
        ],
        "X-Virus-Scanned": [
            "amavisd-new at osuosl.org",
            "amavisd-new at osuosl.org"
        ],
        "X-Greylist": "from auto-whitelisted by SQLgrey-1.7.6",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=herbertland-com.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=LXesf4AsgsEbh+lgKBWaF/2z+sYBYKkQVtrJV1lFngk=;\n\tb=FU/tCHfHx8Cakxb67lV4pEy0kMAHx7e0870/qh88mrNDwENNaBsoqBzK+EY0nBJz5G\n\tRIhmWOWV+MrI0H7GDZbmakaAybsqchlH95TGtnvI+VepCXpFuzdyDnwDoGuL6bOm3lOa\n\tpPFpQvdGt5nVKanB6e/iqPWm2ytpRckPBTQpt2WrniK5bHvfI1q7Qwzme37IalHVcbbD\n\tjk3U6XJtCYhj3xG6PuVFpUX9lI14GWDO6hkZA9hZ75KGRYURuS7SrBUnwFVQSgttuBML\n\tF47fiCtXdHggCMjhklZVzVLUmhqf+6bo5gFGnBRTONsh0KWj86ijXDY3i/5eCthwtr44\n\tXyqA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=LXesf4AsgsEbh+lgKBWaF/2z+sYBYKkQVtrJV1lFngk=;\n\tb=MhoX6zjO1Y6KrtJlwOmwv0a2qkDQZEOoQgb8Tc5RCdBwJlIIkEqY3KJg7ELbzs7Epc\n\tpLr/c5/lBgBOvCT2xjGfw055Of5n+AndTyyuPK2uktPaoMABdU7oQsm3x8nEnNyDDZyN\n\tuLSp+hVofbCqdkOl1US4nL9ONKlh6LknqV0umdNChkolUDt9Enq5inlOHoYqLbOYL7JP\n\tqaoHUCLeHXfApKOGEQcNr88dl+oXOvCQFjUO4oGX/kcm3a3WKhfbsATlSjMoVHRbCYiI\n\tf/t71aNKmqrCbFMZ8MSuP7/itKOACWLOKQRuZ9f0EarIzRI2FP+l3IEbMk4EtLACsDw3\n\t/H2Q==",
        "X-Gm-Message-State": "APjAAAWnpT2D8tp4mBQS6vDzwWL6yuaV88AKw0g7dJIjd+VizcFG6A0K\n\tE2JzW7ymlaPpiU7RHpWOv5/x+zy5NkY=",
        "X-Google-Smtp-Source": "APXvYqx9kcw6ufuS3zbsz+mfrJ0lsnxNWR7ElM90d4ESITaGybwJwmNBE9c2H5zEVQBmA2f9wJuYKw==",
        "X-Received": "by 2002:a17:902:f30f:: with SMTP id\n\tgb15mr11376697plb.233.1565992457820; \n\tFri, 16 Aug 2019 14:54:17 -0700 (PDT)",
        "From": "Tom Herbert <tom@herbertland.com>",
        "To": "Intel-wired-lan@lists.osuosl.org",
        "Date": "Fri, 16 Aug 2019 14:53:44 -0700",
        "Message-Id": "<1565992424-22379-8-git-send-email-tom@herbertland.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<1565992424-22379-1-git-send-email-tom@herbertland.com>",
        "References": "<1565992424-22379-1-git-send-email-tom@herbertland.com>",
        "Subject": "[Intel-wired-lan] [PATCH v2 net-next 7/7] ip6tlvs: Validation of TX\n\tDestination and Hop-by-Hop options",
        "X-BeenThere": "intel-wired-lan@osuosl.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "Intel Wired Ethernet Linux Kernel Driver Development\n\t<intel-wired-lan.osuosl.org>",
        "List-Unsubscribe": "<https://lists.osuosl.org/mailman/options/intel-wired-lan>, \n\t<mailto:intel-wired-lan-request@osuosl.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.osuosl.org/pipermail/intel-wired-lan/>",
        "List-Post": "<mailto:intel-wired-lan@osuosl.org>",
        "List-Help": "<mailto:intel-wired-lan-request@osuosl.org?subject=help>",
        "List-Subscribe": "<https://lists.osuosl.org/mailman/listinfo/intel-wired-lan>, \n\t<mailto:intel-wired-lan-request@osuosl.org?subject=subscribe>",
        "Cc": "Tom Herbert <tom@herbertland.com>, Tom Herbert <tom@quantonium.net>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "intel-wired-lan-bounces@osuosl.org",
        "Sender": "\"Intel-wired-lan\" <intel-wired-lan-bounces@osuosl.org>"
    },
    "content": "From: Tom Herbert <tom@quantonium.net>\n\nValidate Destination and Hop-by-Hop options. This uses the information\nin the TLV parameters table to validate various aspects of both\nindividual TLVs as well as a list of TLVs in an extension header.\n\nThere are two levels of validation that can be performed: simple checks\nand deep checks. Simple checks validate only the most basic properties\nsuch as that the TLV list fits into the EH. Deep checks do a fine\ngrained validation that includes perferred ordering, length limits,\nand length alignment.\n\nWith proper permissions set in the TLV parameter table, this patch\nallows non-privileged users to send TLVs. Given that TLVs are open\nended and potentially a source of DOS attack, deep checks are\nperformed to limit the format that a non-privileged user can send.\nIf deep checks are enabled, a canonical format for sending TLVs is\nenforced (in adherence with the robustness principle). A TLV must\nbe well ordered with respect to the preferred order for the TLV.\nEach TLV must be aligned as described in the parameter table. Minimal\npadding (one padding TLV) is used to align TLVs. The length of the\nextension header as well as the count of non-padding TLVs is checked\nagainst max_*_opts_len and max_*_opts_cnt. For individual TLVs, length\nlimits and length alignment is checked.\n\nSigned-off-by: Tom Herbert <tom@quantonium.net>\nSigned-off-by: Tom Herbert <tom@herbertland.com>\n---\n include/net/ipeh.h        |  22 +++\n net/ipv6/datagram.c       |  51 +++++--\n net/ipv6/exthdrs_common.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++\n net/ipv6/ipv6_sockglue.c  |  39 ++---\n 4 files changed, 455 insertions(+), 39 deletions(-)",
    "diff": "diff --git a/include/net/ipeh.h b/include/net/ipeh.h\nindex 7358ed9..491dfdb 100644\n--- a/include/net/ipeh.h\n+++ b/include/net/ipeh.h\n@@ -154,6 +154,28 @@ struct ipv6_txoptions *ipeh_renew_options(struct sock *sk,\n struct ipv6_txoptions *ipeh_fixup_options(struct ipv6_txoptions *opt_space,\n \t\t\t\t\t  struct ipv6_txoptions *opt);\n \n+int ipeh_opt_validate_tlvs(struct net *net,\n+\t\t\t   struct tlv_param_table *tlv_param_table,\n+\t\t\t   struct ipv6_opt_hdr *opt,\n+\t\t\t   unsigned int optname, bool admin,\n+\t\t\t   unsigned int max_len, unsigned int max_cnt);\n+int ipeh_opt_validate_single_tlv(struct net *net,\n+\t\t\t\t struct tlv_param_table *tlv_param_table,\n+\t\t\t\t unsigned int optname, const __u8 *tlv,\n+\t\t\t\t size_t len, bool deleting, bool admin);\n+int ipeh_opt_check_perm(struct net *net,\n+\t\t\tstruct tlv_param_table *tlv_param_table,\n+\t\t\tstruct ipv6_txoptions *txopt, int optname, bool admin);\n+\n+struct ipv6_txoptions *ipeh_txopt_from_opt(struct sock *sk,\n+\t\t\t\t\t   struct tlv_param_table\n+\t\t\t\t\t\t*tlv_param_table,\n+\t\t\t\t\t   struct ipv6_txoptions *opt,\n+\t\t\t\t\t   int optname, char __user *optval,\n+\t\t\t\t\t   unsigned int optlen,\n+\t\t\t\t\t   unsigned int max_len,\n+\t\t\t\t\t   unsigned int max_cnt);\n+\n /* Generic extension header TLV parser */\n \n enum ipeh_parse_errors {\ndiff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c\nindex 9ab897d..4b87773 100644\n--- a/net/ipv6/datagram.c\n+++ b/net/ipv6/datagram.c\n@@ -837,7 +837,10 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\tbreak;\n \n \t\tcase IPV6_2292HOPOPTS:\n-\t\tcase IPV6_HOPOPTS:\n+\t\tcase IPV6_HOPOPTS: {\n+\t\t\tint max_len = net->ipv6.sysctl.max_hbh_opts_len;\n+\t\t\tint max_cnt = net->ipv6.sysctl.max_hbh_opts_cnt;\n+\n \t\t\tif (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n@@ -849,15 +852,24 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n \t\t\t}\n-\t\t\tif (!ns_capable(net->user_ns, CAP_NET_RAW)) {\n-\t\t\t\terr = -EPERM;\n+\n+\t\t\terr = ipeh_opt_validate_tlvs(net, &ipv6_tlv_param_table,\n+\t\t\t\t\t\t     hdr, IPV6_HOPOPTS,\n+\t\t\t\t\t\t     ns_capable(net->user_ns,\n+\t\t\t\t\t\t\t\tCAP_NET_RAW),\n+\t\t\t\t\t\t     max_len, max_cnt);\n+\t\t\tif (err < 0)\n \t\t\t\tgoto exit_f;\n-\t\t\t}\n+\n \t\t\topt->opt_nflen += len;\n \t\t\topt->hopopt = hdr;\n \t\t\tbreak;\n+\t\t}\n+\n+\t\tcase IPV6_2292DSTOPTS: {\n+\t\t\tint max_len = net->ipv6.sysctl.max_dst_opts_len;\n+\t\t\tint max_cnt = net->ipv6.sysctl.max_dst_opts_cnt;\n \n-\t\tcase IPV6_2292DSTOPTS:\n \t\t\tif (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n@@ -869,10 +881,14 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n \t\t\t}\n-\t\t\tif (!ns_capable(net->user_ns, CAP_NET_RAW)) {\n-\t\t\t\terr = -EPERM;\n+\t\t\terr = ipeh_opt_validate_tlvs(net, &ipv6_tlv_param_table,\n+\t\t\t\t\t\t     hdr, IPV6_DSTOPTS,\n+\t\t\t\t\t\t     ns_capable(net->user_ns,\n+\t\t\t\t\t\t\t\tCAP_NET_RAW),\n+\t\t\t\t\t\t     max_len, max_cnt);\n+\t\t\tif (err < 0)\n \t\t\t\tgoto exit_f;\n-\t\t\t}\n+\n \t\t\tif (opt->dst1opt) {\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n@@ -880,9 +896,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\topt->opt_flen += len;\n \t\t\topt->dst1opt = hdr;\n \t\t\tbreak;\n+\t\t}\n \n \t\tcase IPV6_DSTOPTS:\n-\t\tcase IPV6_RTHDRDSTOPTS:\n+\t\tcase IPV6_RTHDRDSTOPTS: {\n+\t\t\tint max_len = net->ipv6.sysctl.max_dst_opts_len;\n+\t\t\tint max_cnt = net->ipv6.sysctl.max_dst_opts_cnt;\n+\n \t\t\tif (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n@@ -894,10 +914,15 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\t\terr = -EINVAL;\n \t\t\t\tgoto exit_f;\n \t\t\t}\n-\t\t\tif (!ns_capable(net->user_ns, CAP_NET_RAW)) {\n-\t\t\t\terr = -EPERM;\n+\n+\t\t\terr = ipeh_opt_validate_tlvs(net, &ipv6_tlv_param_table,\n+\t\t\t\t\t\t     hdr, IPV6_DSTOPTS,\n+\t\t\t\t\t\t     ns_capable(net->user_ns,\n+\t\t\t\t\t\t\t\tCAP_NET_RAW),\n+\t\t\t\t\t\t     max_len, max_cnt);\n+\t\t\tif (err < 0)\n \t\t\t\tgoto exit_f;\n-\t\t\t}\n+\n \t\t\tif (cmsg->cmsg_type == IPV6_DSTOPTS) {\n \t\t\t\topt->opt_flen += len;\n \t\t\t\topt->dst1opt = hdr;\n@@ -906,7 +931,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,\n \t\t\t\topt->dst0opt = hdr;\n \t\t\t}\n \t\t\tbreak;\n-\n+\t\t}\n \t\tcase IPV6_2292RTHDR:\n \t\tcase IPV6_RTHDR:\n \t\t\tif (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {\ndiff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c\nindex b44c6fd..5df55bd 100644\n--- a/net/ipv6/exthdrs_common.c\n+++ b/net/ipv6/exthdrs_common.c\n@@ -262,6 +262,318 @@ bool ipeh_parse_tlv(unsigned int class,\n }\n EXPORT_SYMBOL(ipeh_parse_tlv);\n \n+/* TLV validation functions */\n+\n+/* Validate a single non-padding TLV */\n+static int __ipeh_opt_validate_single_tlv(struct net *net, const __u8 *tlv,\n+\t\t\t\t\t  struct tlv_proc *tproc,\n+\t\t\t\t\t  unsigned int class, bool *deep_check,\n+\t\t\t\t\t  bool deleting, bool admin)\n+{\n+\tstruct tlv_tx_params *tptx = &tproc->params.t;\n+\n+\tif (tlv[0] < 2) /* Must be non-padding */\n+\t\treturn -EINVAL;\n+\n+\t/* Check permissions */\n+\tswitch (admin ? tptx->admin_perm : tptx->user_perm) {\n+\tcase IPEH_TLV_PERM_NO_CHECK:\n+\t\t/* Allowed with no deep checks */\n+\t\t*deep_check = false;\n+\t\treturn 0;\n+\tcase IPEH_TLV_PERM_WITH_CHECK:\n+\t\t/* Allowed with deep checks */\n+\t\t*deep_check = true;\n+\t\tbreak;\n+\tdefault:\n+\t\t/* No permission */\n+\t\treturn -EPERM;\n+\t}\n+\n+\t/* Perform deep checks on the TLV */\n+\n+\t/* Check class */\n+\tif ((tptx->class & class) != class)\n+\t\treturn -EINVAL;\n+\n+\t/* Don't bother checking lengths when deleting, the TLV is only\n+\t * needed here for lookup\n+\t */\n+\tif (deleting) {\n+\t\t/* Don't bother with deep checks when deleting */\n+\t\t*deep_check = false;\n+\t} else {\n+\t\t/* Check length */\n+\t\tif (tlv[1] < tptx->min_data_len || tlv[1] > tptx->max_data_len)\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* Check length alignment */\n+\t\tif ((tlv[1] % (tptx->data_len_mult + 1)) != tptx->data_len_off)\n+\t\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static unsigned int optname_to_tlv_class(int optname)\n+{\n+\tswitch (optname) {\n+\tcase IPV6_HOPOPTS:\n+\t\treturn IPEH_TLV_CLASS_FLAG_HOPOPT;\n+\tcase IPV6_RTHDRDSTOPTS:\n+\t\treturn IPEH_TLV_CLASS_FLAG_RTRDSTOPT;\n+\tcase IPV6_DSTOPTS:\n+\t\treturn IPEH_TLV_CLASS_FLAG_DSTOPT;\n+\tdefault:\n+\t\treturn -1U;\n+\t}\n+}\n+\n+static int __ipeh_opt_validate_tlvs(struct net *net,\n+\t\t\t\t    struct tlv_param_table *tlv_param_table,\n+\t\t\t\t    struct ipv6_opt_hdr *opt,\n+\t\t\t\t    unsigned int optname, bool deleting,\n+\t\t\t\t    bool admin, unsigned int max_len,\n+\t\t\t\t    unsigned int max_cnt)\n+{\n+\tbool deep_check = !admin, did_deep_check = false;\n+\tunsigned int opt_len, tlv_len, offset;\n+\tunsigned int padding = 0, numpad = 0;\n+\tunsigned short prev_tlv_order = 0;\n+\tunsigned int class, cnt = 0;\n+\tstruct tlv_tx_params *tptx;\n+\tint retc, ret = -EINVAL;\n+\t__u8 *tlv = (__u8 *)opt;\n+\tstruct tlv_proc *tproc;\n+\n+\topt_len = ipv6_optlen(opt);\n+\toffset = sizeof(*opt);\n+\n+\tclass = optname_to_tlv_class(optname);\n+\n+\trcu_read_lock();\n+\n+\twhile (offset < opt_len) {\n+\t\tswitch (tlv[offset]) {\n+\t\tcase IPV6_TLV_PAD1:\n+\t\t\ttlv_len = 1;\n+\t\t\tpadding++;\n+\t\t\tnumpad++;\n+\t\t\tbreak;\n+\t\tcase IPV6_TLV_PADN:\n+\t\t\tif (offset + 1 >= opt_len)\n+\t\t\t\tgoto out;\n+\n+\t\t\ttlv_len = tlv[offset + 1] + 2;\n+\n+\t\t\tif (offset + tlv_len > opt_len)\n+\t\t\t\tgoto out;\n+\n+\t\t\tpadding += tlv_len;\n+\t\t\tnumpad++;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tif (offset + 1 >= opt_len)\n+\t\t\t\tgoto out;\n+\n+\t\t\ttlv_len = tlv[offset + 1] + 2;\n+\n+\t\t\tif (offset + tlv_len > opt_len)\n+\t\t\t\tgoto out;\n+\n+\t\t\ttproc = ipeh_tlv_get_proc(tlv_param_table,\n+\t\t\t\t\t\t  &tlv[offset]);\n+\t\t\ttptx = &tproc->params.t;\n+\n+\t\t\tretc = __ipeh_opt_validate_single_tlv(net, &tlv[offset],\n+\t\t\t\t\t\t\t      tproc, class,\n+\t\t\t\t\t\t\t      &deep_check,\n+\t\t\t\t\t\t\t      deleting, admin);\n+\t\t\tif (retc < 0) {\n+\t\t\t\tret = retc;\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\n+\t\t\tif (deep_check) {\n+\t\t\t\t/* Check for too many options */\n+\t\t\t\tif (++cnt > max_cnt) {\n+\t\t\t\t\tret = -E2BIG;\n+\t\t\t\t\tgoto out;\n+\t\t\t\t}\n+\n+\t\t\t\t/* Check order */\n+\t\t\t\tif (tptx->preferred_order < prev_tlv_order)\n+\t\t\t\t\tgoto out;\n+\n+\t\t\t\t/* Check alignment */\n+\t\t\t\tif ((offset % (tptx->align_mult + 1)) !=\n+\t\t\t\t    tptx->align_off)\n+\t\t\t\t\tgoto out;\n+\n+\t\t\t\t/* Check for right amount of padding */\n+\t\t\t\tif (numpad > 1 || padding > tptx->align_mult)\n+\t\t\t\t\tgoto out;\n+\n+\t\t\t\tprev_tlv_order = tptx->preferred_order;\n+\n+\t\t\t\tdid_deep_check = true;\n+\t\t\t}\n+\t\t\tpadding = 0;\n+\t\t\tnumpad = 0;\n+\t\t}\n+\t\toffset += tlv_len;\n+\t}\n+\n+\t/* Check trailing padding. Note this covers the case option list\n+\t * only contains padding.\n+\t */\n+\tif (deep_check && (numpad > 1 || padding > 7))\n+\t\tgoto out;\n+\n+\t/* If we did at least one deep check apply length limit */\n+\tif (did_deep_check && opt_len > max_len) {\n+\t\tret = -EMSGSIZE;\n+\t\tgoto out;\n+\t}\n+\n+\t/* All good */\n+\tret = 0;\n+out:\n+\trcu_read_unlock();\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * ipeh_opt_validate_tlvs - Validate TLVs.\n+ * @net: Current net\n+ * @tlv_param_table: TLV parameter table\n+ * @opt: The option header\n+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS\n+ * @admin: Set for privileged user\n+ * @max_len: Maximum length for TLV\n+ * @max_cnt: Maximum number of non-padding TLVs\n+ *\n+ * Description:\n+ * Walks the TLVs in a list to verify that the TLV lengths and other\n+ * parameters are in bounds for a Destination or Hop-by-Hop option.\n+ * Return -EINVAL is there is a problem, zero otherwise.\n+ */\n+int ipeh_opt_validate_tlvs(struct net *net,\n+\t\t\t   struct tlv_param_table *tlv_param_table,\n+\t\t\t   struct ipv6_opt_hdr *opt, unsigned int optname,\n+\t\t\t   bool admin, unsigned int max_len,\n+\t\t\t   unsigned int max_cnt)\n+{\n+\treturn __ipeh_opt_validate_tlvs(net, tlv_param_table, opt, optname,\n+\t\t\t\t\tfalse, admin, max_len, max_cnt);\n+}\n+EXPORT_SYMBOL(ipeh_opt_validate_tlvs);\n+\n+/**\n+ * ipeh_opt_validate_single_tlv - Check that a single TLV is valid.\n+ * @net: Current net\n+ * @tlv_param_table: TLV parameter table\n+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS\n+ * @tlv: The TLV as array of bytes\n+ * @len: Length of buffer holding TLV\n+ * @deleting: TLV is being deleted\n+ * @admin: Set for privileged user\n+ *\n+ * Description:\n+ * Validates a single TLV. The TLV must be non-padding type. The length\n+ * of the TLV (as determined by the second byte that gives length of the\n+ * option data) must match @len.\n+ */\n+int ipeh_opt_validate_single_tlv(struct net *net,\n+\t\t\t\t struct tlv_param_table *tlv_param_table,\n+\t\t\t\t unsigned int optname, const __u8 *tlv,\n+\t\t\t\t size_t len, bool deleting, bool admin)\n+{\n+\tstruct tlv_proc *tproc;\n+\tunsigned int class;\n+\tbool deep_check;\n+\tint ret = 0;\n+\n+\tclass = optname_to_tlv_class(optname);\n+\n+\tif (tlv[0] < 2)\n+\t\treturn -EINVAL;\n+\n+\tif (len < 2)\n+\t\treturn -EINVAL;\n+\n+\tif (tlv[1] + 2 != len)\n+\t\treturn -EINVAL;\n+\n+\trcu_read_lock();\n+\n+\ttproc = ipeh_tlv_get_proc(tlv_param_table, tlv);\n+\n+\tret = __ipeh_opt_validate_single_tlv(net, tlv, tproc, class,\n+\t\t\t\t\t     &deep_check, deleting, admin);\n+\n+\trcu_read_unlock();\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL(ipeh_opt_validate_single_tlv);\n+\n+/**\n+ * ipeh_opt_check_perm - Check that current capabilities allows modifying\n+ * txopts.\n+ * @net: Current net\n+ * @tlv_param_table: TLV parameter table\n+ * @txopts: TX options from the socket\n+ * @optname: IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, or IPV6_DSTOPTS\n+ * @admin: Set for privileged user\n+ *\n+ * Description:\n+ *\n+ * Checks whether the permissions of TLV that are set on a socket permit\n+ * modificationr.\n+ *\n+ */\n+int ipeh_opt_check_perm(struct net *net,\n+\t\t\tstruct tlv_param_table *tlv_param_table,\n+\t\t\tstruct ipv6_txoptions *txopt, int optname, bool admin)\n+{\n+\tstruct ipv6_opt_hdr *opt;\n+\tint retv = -EPERM;\n+\n+\tif (!txopt)\n+\t\treturn 0;\n+\n+\tswitch (optname) {\n+\tcase IPV6_HOPOPTS:\n+\t\topt = txopt->hopopt;\n+\t\tbreak;\n+\tcase IPV6_RTHDRDSTOPTS:\n+\t\topt = txopt->dst0opt;\n+\t\tbreak;\n+\tcase IPV6_DSTOPTS:\n+\t\topt = txopt->dst1opt;\n+\t\tbreak;\n+\tdefault:\n+\t\tgoto out;\n+\t}\n+\n+\tif (!opt) {\n+\t\tretv = 0;\n+\t\tgoto out;\n+\t}\n+\n+\t/* Just call the validate function on the options as being\n+\t * deleted.\n+\t */\n+\tretv = __ipeh_opt_validate_tlvs(net, tlv_param_table, opt, optname,\n+\t\t\t\t\ttrue, admin, -1U, -1U);\n+\n+out:\n+\treturn retv;\n+}\n+EXPORT_SYMBOL(ipeh_opt_check_perm);\n+\n /* TLV parameter table functions and structures */\n \n /* Default (unset) values for TLV parameters */\n@@ -454,6 +766,76 @@ int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,\n }\n EXPORT_SYMBOL(__ipeh_tlv_unset);\n \n+/* Utility function tp create TX options from a setsockopt that is setting\n+ * options on a socket.\n+ */\n+struct ipv6_txoptions *ipeh_txopt_from_opt(struct sock *sk,\n+\t\t\t\t\t   struct tlv_param_table\n+\t\t\t\t\t\t\t*tlv_param_table,\n+\t\t\t\t\t   struct ipv6_txoptions *opt,\n+\t\t\t\t\t   int optname, char __user *optval,\n+\t\t\t\t\t   unsigned int optlen,\n+\t\t\t\t\t   unsigned int max_len,\n+\t\t\t\t\t   unsigned int max_cnt)\n+{\n+\tstruct ipv6_opt_hdr *new = NULL;\n+\tstruct net *net = sock_net(sk);\n+\tint retv;\n+\n+\t/* remove any sticky options header with a zero option\n+\t * length, per RFC3542.\n+\t */\n+\tif (optlen == 0) {\n+\t\toptval = NULL;\n+\t} else if (!optval) {\n+\t\treturn ERR_PTR(-EINVAL);\n+\t} else if (optlen < sizeof(struct ipv6_opt_hdr) ||\n+\t\t optlen & 0x7 || optlen > 8 * 255) {\n+\t\treturn ERR_PTR(-EINVAL);\n+\t} else {\n+\t\tnew = memdup_user(optval, optlen);\n+\t\tif (IS_ERR(new))\n+\t\t\treturn (struct ipv6_txoptions *)new;\n+\t\tif (unlikely(ipv6_optlen(new) > optlen)) {\n+\t\t\tkfree(new);\n+\t\t\treturn ERR_PTR(-EINVAL);\n+\t\t}\n+\t}\n+\n+\tif (optname != IPV6_RTHDR) {\n+\t\tbool cap = ns_capable(net->user_ns, CAP_NET_RAW);\n+\n+\t\t/* First check if we have permission to delete\n+\t\t * the existing options on the socket.\n+\t\t */\n+\t\tretv = ipeh_opt_check_perm(net, tlv_param_table,\n+\t\t\t\t\t   opt, optname, cap);\n+\t\tif (retv < 0) {\n+\t\t\tkfree(new);\n+\t\t\treturn ERR_PTR(retv);\n+\t\t}\n+\n+\t\t/* Check permissions and other validations on new\n+\t\t * TLVs\n+\t\t */\n+\t\tif (new) {\n+\t\t\tretv = ipeh_opt_validate_tlvs(net, tlv_param_table,\n+\t\t\t\t\t\t      new, optname, cap,\n+\t\t\t\t\t\t      max_len, max_cnt);\n+\t\t\tif (retv < 0) {\n+\t\t\t\tkfree(new);\n+\t\t\t\treturn ERR_PTR(retv);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\topt = ipeh_renew_options(sk, opt, optname, new);\n+\tkfree(new);\n+\n+\treturn opt;\n+}\n+EXPORT_SYMBOL(ipeh_txopt_from_opt);\n+\n const struct nla_policy ipeh_tlv_nl_policy[IPEH_TLV_ATTR_MAX + 1] = {\n \t[IPEH_TLV_ATTR_TYPE] =\t\t{ .type = NLA_U8, },\n \t[IPEH_TLV_ATTR_ORDER] =\t\t{ .type = NLA_U16, },\ndiff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c\nindex 8755ecc..b8e007ca 100644\n--- a/net/ipv6/ipv6_sockglue.c\n+++ b/net/ipv6/ipv6_sockglue.c\n@@ -395,40 +395,27 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,\n \tcase IPV6_RTHDR:\n \tcase IPV6_DSTOPTS:\n \t{\n+\t\tunsigned int max_len = -1U, max_cnt = -1U;\n \t\tstruct ipv6_txoptions *opt;\n-\t\tstruct ipv6_opt_hdr *new = NULL;\n \n-\t\t/* hop-by-hop / destination options are privileged option */\n-\t\tretv = -EPERM;\n-\t\tif (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))\n+\t\tswitch (optname) {\n+\t\tcase IPV6_HOPOPTS:\n+\t\t\tmax_len = net->ipv6.sysctl.max_hbh_opts_len;\n+\t\t\tmax_cnt = net->ipv6.sysctl.max_hbh_opts_cnt;\n \t\t\tbreak;\n-\n-\t\t/* remove any sticky options header with a zero option\n-\t\t * length, per RFC3542.\n-\t\t */\n-\t\tif (optlen == 0)\n-\t\t\toptval = NULL;\n-\t\telse if (!optval)\n-\t\t\tgoto e_inval;\n-\t\telse if (optlen < sizeof(struct ipv6_opt_hdr) ||\n-\t\t\t optlen & 0x7 || optlen > 8 * 255)\n-\t\t\tgoto e_inval;\n-\t\telse {\n-\t\t\tnew = memdup_user(optval, optlen);\n-\t\t\tif (IS_ERR(new)) {\n-\t\t\t\tretv = PTR_ERR(new);\n+\t\tcase IPV6_RTHDRDSTOPTS:\n+\t\tcase IPV6_DSTOPTS:\n+\t\t\tmax_len = net->ipv6.sysctl.max_dst_opts_len;\n+\t\t\tmax_cnt = net->ipv6.sysctl.max_dst_opts_cnt;\n \t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (unlikely(ipv6_optlen(new) > optlen)) {\n-\t\t\t\tkfree(new);\n-\t\t\t\tgoto e_inval;\n-\t\t\t}\n \t\t}\n \n \t\topt = rcu_dereference_protected(np->opt,\n \t\t\t\t\t\tlockdep_sock_is_held(sk));\n-\t\topt = ipeh_renew_options(sk, opt, optname, new);\n-\t\tkfree(new);\n+\t\topt = ipeh_txopt_from_opt(sk, &ipv6_tlv_param_table, opt,\n+\t\t\t\t\t  optname, optval, optlen, max_len,\n+\t\t\t\t\t  max_cnt);\n+\n \t\tif (IS_ERR(opt)) {\n \t\t\tretv = PTR_ERR(opt);\n \t\t\tbreak;\n",
    "prefixes": [
        "v2",
        "net-next",
        "7/7"
    ]
}