get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 1145969,
    "url": "http://patchwork.ozlabs.org/api/patches/1145969/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/patch/1565657261-15979-5-git-send-email-tom@quantonium.net/",
    "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": "<1565657261-15979-5-git-send-email-tom@quantonium.net>",
    "list_archive_url": null,
    "date": "2019-08-13T00:47:38",
    "name": "[net-next,4/7] ip6tlvs: Registration of TLV handlers and parameters",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "f5b367bbf84a1532749f306e5704ab644f3e40b3",
    "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/1565657261-15979-5-git-send-email-tom@quantonium.net/mbox/",
    "series": [
        {
            "id": 124709,
            "url": "http://patchwork.ozlabs.org/api/series/124709/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/intel-wired-lan/list/?series=124709",
            "date": "2019-08-13T00:47:39",
            "name": "ipv6: Extension header infrastructure",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/124709/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1145969/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1145969/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.136; helo=silver.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=\"iqoNoV86\"; dkim-atps=neutral"
        ],
        "Received": [
            "from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136])\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 466vPv1ZCbz9sND\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 13 Aug 2019 10:54:59 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby silver.osuosl.org (Postfix) with ESMTP id C8D512265B;\n\tTue, 13 Aug 2019 00:54:57 +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 Sa1FoGHBA65u; Tue, 13 Aug 2019 00:54:51 +0000 (UTC)",
            "from ash.osuosl.org (ash.osuosl.org [140.211.166.34])\n\tby silver.osuosl.org (Postfix) with ESMTP id 2880D2264A;\n\tTue, 13 Aug 2019 00:54:51 +0000 (UTC)",
            "from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137])\n\tby ash.osuosl.org (Postfix) with ESMTP id AECB91BF989\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tTue, 13 Aug 2019 00:54:49 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n\tby fraxinus.osuosl.org (Postfix) with ESMTP id AA7CB84465\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tTue, 13 Aug 2019 00:54:49 +0000 (UTC)",
            "from fraxinus.osuosl.org ([127.0.0.1])\n\tby localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)\n\twith ESMTP id kISn1OeqWiQ4 for <Intel-wired-lan@lists.osuosl.org>;\n\tTue, 13 Aug 2019 00:54:48 +0000 (UTC)",
            "from mail-pl1-f196.google.com (mail-pl1-f196.google.com\n\t[209.85.214.196])\n\tby fraxinus.osuosl.org (Postfix) with ESMTPS id 6333E842E9\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tTue, 13 Aug 2019 00:54:48 +0000 (UTC)",
            "by mail-pl1-f196.google.com with SMTP id m9so48380209pls.8\n\tfor <Intel-wired-lan@lists.osuosl.org>;\n\tMon, 12 Aug 2019 17:54:48 -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\t14sm105671426pfy.40.2019.08.12.17.48.07\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tMon, 12 Aug 2019 17:48:08 -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=/0xar3guqiUxzkMFpxzEUC/GRPHvu0hLKBf9XEXt4AU=;\n\tb=iqoNoV868XAO9CDNfl9K9oMDTrIPlj2FFLVxfwcTJbdN0eRbZg1UBMUo/fQmD15nIf\n\tsjsMDFDpmxcUnaI5j7kkLtNlK+Oh4McQQp7gaLPPQ3IaJoWHvfy93j2xuWUerOUlsfkP\n\tl/KGV7qVL6yJsqrz8tG8kRfJBrPl+NA+2gwSpLqYdXhGo4PT/ZPjBsIOdS3YM+yCtetB\n\tAKCVaZ4vm1TtrskxO5QXOehoQKhkMyKewF37OYyMpKXfS4JhlbSQiu0GZf/woEOm44h/\n\t2t6UDRTQV2YUbPrNACAYJ3hWmrj+237bPSHyHaXZiV6Pl7IdPYpHbic1u/mYcVzUPyVj\n\tv/Ng==",
        "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=/0xar3guqiUxzkMFpxzEUC/GRPHvu0hLKBf9XEXt4AU=;\n\tb=kLzgDY5L8tlOV9QJ6/atEk5Pg0kMIvwKhdAwXzZgkwhGtBVkcL93Aix4ZU7ILN4pDb\n\t4YWv+4q0V4jBHDsxuNG2tclMhDy/bRAXkmZPu0zQ6JY4KdzRSPp7lYKEpNme76KJvKSw\n\tDxvciIZ0x6vdA3q+5l1R0xB+ekJ8RYxWJ+o7uPOLo3KfodssZPaoVH67L1x8I0ZzLrf4\n\txIe/3H2RK+3PNYj3wC2CflP8Lm9RnuX0HytA7hOd5SkcVHavi403PkNCY/RtTbwkj0hx\n\t0ls9C5p/5cRscHCReYa9PXuEk3trC7roeKUdlUMDiHTLqzPhLq4feZ7XRPayHs5Aemx7\n\tJsaA==",
        "X-Gm-Message-State": "APjAAAXqsNUiRkKHFlUdlFJXBkC/J5fFYFdVaDEl3f0J3bo4DLcd7htN\n\tVTY9Q8ziJ2yqPYwgL8E46zlaOCt+V2w=",
        "X-Google-Smtp-Source": "APXvYqzm9f/HzpoDYKuLmzRFtA8QqeccchRZrDJvyVttzLwTIWlJ/uSjzGQpzAX/ej508/CkkeQiZQ==",
        "X-Received": "by 2002:a17:902:d90a:: with SMTP id\n\tc10mr34315786plz.208.1565657288805; \n\tMon, 12 Aug 2019 17:48:08 -0700 (PDT)",
        "From": "Tom Herbert <tom@herbertland.com>",
        "X-Google-Original-From": "Tom Herbert <tom@quantonium.net>",
        "To": "Intel-wired-lan@lists.osuosl.org",
        "Date": "Mon, 12 Aug 2019 17:47:38 -0700",
        "Message-Id": "<1565657261-15979-5-git-send-email-tom@quantonium.net>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<1565657261-15979-1-git-send-email-tom@quantonium.net>",
        "References": "<1565657261-15979-1-git-send-email-tom@quantonium.net>",
        "Subject": "[Intel-wired-lan] [PATCH net-next 4/7] ip6tlvs: Registration of TLV\n\thandlers and parameters",
        "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@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": "Create a single TLV parameter table that holds meta information for IPv6\nHop-by-Hop and Destination TLVs. The data structure is composed of a 256\nelement array of u8's (one entry for each TLV type to allow O(1)\nlookup). Each entry provides an offset into an array of TLV proc data\nstructures which follows the array of u8s. The TLV proc data structure\ncontains parameters and handler functions for receiving and transmitting\nTLVs. The zeroth element in the TLV proc array provides default\nparameters for TLVs.\n\nA class attribute indicates the type of extension header in which the\nTLV may be used (e.g. Hop-by-Hop options, Destination options, or\nDestination options before the routing header).\n\nFunctions are defined to manipulate entries in the TLV parameter table.\n\n* tlv_{set|unset}_proc set a TLV proc entry (ops and parameters)\n* tlv_{set|unset}_params set parameters only\n\nReceive TLV lookup and processing is modified to be a lookup in the TLV\nparameter table. An init table containing parameters for TLVs supported\nby the kernel is used to initialize the TLV table.\n\nSigned-off-by: Tom Herbert <tom@quantonium.net>\n---\n include/net/ipeh.h         | 107 ++++++++++++++++--\n include/net/ipv6.h         |   3 +\n include/uapi/linux/ipeh.h  |  16 +++\n net/ipv6/exthdrs.c         |  14 ++-\n net/ipv6/exthdrs_common.c  | 271 +++++++++++++++++++++++++++++++++++++++++----\n net/ipv6/exthdrs_options.c |  63 +++++++----\n 6 files changed, 421 insertions(+), 53 deletions(-)\n create mode 100644 include/uapi/linux/ipeh.h",
    "diff": "diff --git a/include/net/ipeh.h b/include/net/ipeh.h\nindex 8f97b4d..816fe36 100644\n--- a/include/net/ipeh.h\n+++ b/include/net/ipeh.h\n@@ -9,13 +9,105 @@\n  *     and false, if it failed.\n  *     It MUST NOT touch skb->h.\n  */\n-struct tlvtype_proc {\n-\tint\ttype;\n-\tbool\t(*func)(struct sk_buff *skb, int offset);\n+struct tlv_ops {\n+\tbool\t(*func)(unsigned int class, struct sk_buff *skb, int offset);\n };\n \n-extern const struct tlvtype_proc tlvprocdestopt_lst[];\n-extern const struct tlvtype_proc tlvprochopopt_lst[];\n+struct tlv_rx_params {\n+\tunsigned char class : 4;\n+};\n+\n+struct tlv_tx_params {\n+};\n+\n+struct tlv_params {\n+\tstruct tlv_rx_params r;\n+\tstruct tlv_tx_params t;\n+};\n+\n+struct tlv_proc {\n+\tstruct tlv_ops ops;\n+\tstruct tlv_params params;\n+};\n+\n+struct tlv_type {\n+\tstruct tlv_proc proc;\n+};\n+\n+struct tlv_proc_init {\n+\tint type;\n+\tstruct tlv_proc proc;\n+};\n+\n+struct tlv_param_table_data {\n+\tunsigned char entries[256];\n+\tunsigned char count;\n+\tstruct rcu_head rcu;\n+\tstruct tlv_type types[0];\n+};\n+\n+struct tlv_param_table {\n+\tstruct tlv_param_table_data __rcu *data;\n+};\n+\n+extern struct tlv_param_table ipv6_tlv_param_table;\n+\n+int __ipeh_tlv_set(struct tlv_param_table *tlv_param_table,\n+\t\t   unsigned char type, const struct tlv_params *params,\n+\t\t   const struct tlv_ops *ops);\n+\n+static inline int ipeh_tlv_set_params(struct tlv_param_table *tlv_param_table,\n+\t\t\t\t      unsigned char type,\n+\t\t\t\t      const struct tlv_params *params)\n+{\n+\treturn __ipeh_tlv_set(tlv_param_table, type, params, NULL);\n+}\n+\n+static inline int ipeh_tlv_set_proc(struct tlv_param_table *tlv_param_table,\n+\t\t\t       unsigned char type,\n+\t\t\t       const struct tlv_proc *proc)\n+{\n+\treturn __ipeh_tlv_set(tlv_param_table, type,\n+\t\t\t      &proc->params, &proc->ops);\n+}\n+\n+int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,\n+\t\t     unsigned char type, bool params_only);\n+\n+static inline int ipeh_tlv_unset_params(struct tlv_param_table *tlv_param_table,\n+\t\t\t\t\tunsigned char type)\n+{\n+\treturn __ipeh_tlv_unset(tlv_param_table, type, true);\n+}\n+\n+static inline int ipeh_tlv_unset_proc(struct tlv_param_table *tlv_param_table,\n+\t\t\t\t      unsigned char type)\n+{\n+\treturn __ipeh_tlv_unset(tlv_param_table, type, false);\n+}\n+\n+/* ipeh_tlv_get_proc_by_type assumes rcu_read_lock is held */\n+static inline struct tlv_proc *ipeh_tlv_get_proc_by_type(\n+\t\tstruct tlv_param_table *tlv_param_table, unsigned char type)\n+{\n+\tstruct tlv_param_table_data *tpt =\n+\t\t\t\trcu_dereference(tlv_param_table->data);\n+\n+\treturn &tpt->types[tpt->entries[type]].proc;\n+}\n+\n+/* ipeh_tlv_get_proc assumes rcu_read_lock is held */\n+static inline struct tlv_proc *ipeh_tlv_get_proc(\n+\t\tstruct tlv_param_table *tlv_param_table,\n+\t\tconst __u8 *tlv)\n+{\n+\treturn ipeh_tlv_get_proc_by_type(tlv_param_table, tlv[0]);\n+}\n+\n+int ipeh_exthdrs_init(struct tlv_param_table *tlv_param_table,\n+\t\t      const struct tlv_proc_init *init_params,\n+\t\t      int num_init_params);\n+void ipeh_exthdrs_fini(struct tlv_param_table *tlv_param_table);\n \n struct ipv6_txoptions *ipeh_dup_options(struct sock *sk,\n \t\t\t\t\tstruct ipv6_txoptions *opt);\n@@ -46,8 +138,9 @@ enum ipeh_parse_errors {\n #define IPEH_TLV_PAD1\t0\n #define IPEH_TLV_PADN\t1\n \n-bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n-\t\t    int max_count, int off, int len,\n+bool ipeh_parse_tlv(unsigned int class,\n+\t\t    struct tlv_param_table *tlv_param_table,\n+\t\t    struct sk_buff *skb, int max_count, int off, int len,\n \t\t    bool (*parse_error)(struct sk_buff *skb,\n \t\t\t\t\tint off, enum ipeh_parse_errors error));\n \ndiff --git a/include/net/ipv6.h b/include/net/ipv6.h\nindex 1c6878b..07bafad 100644\n--- a/include/net/ipv6.h\n+++ b/include/net/ipv6.h\n@@ -429,6 +429,9 @@ int ip6_ra_control(struct sock *sk, int sel);\n \n int ipv6_parse_hopopts(struct sk_buff *skb);\n \n+int ipv6_exthdrs_options_init(void);\n+void ipv6_exthdrs_options_exit(void);\n+\n bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,\n \t\t       const struct inet6_skb_parm *opt);\n struct ipv6_txoptions *ipv6_update_options(struct sock *sk,\ndiff --git a/include/uapi/linux/ipeh.h b/include/uapi/linux/ipeh.h\nnew file mode 100644\nindex 0000000..c4302b7\n--- /dev/null\n+++ b/include/uapi/linux/ipeh.h\n@@ -0,0 +1,16 @@\n+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */\n+/* ipeh.h - IP extension header TLV management */\n+\n+#ifndef _UAPI_LINUX_IPEH_H\n+#define _UAPI_LINUX_IPEH_H\n+\n+/* Flags for EH type that can use a TLV option */\n+#define IPEH_TLV_CLASS_FLAG_HOPOPT\tBIT(0)\n+#define IPEH_TLV_CLASS_FLAG_RTRDSTOPT\tBIT(1)\n+#define IPEH_TLV_CLASS_FLAG_DSTOPT\tBIT(2)\n+\n+#define IPEH_TLV_CLASS_FLAG_MASK (IPEH_TLV_CLASS_FLAG_HOPOPT |\t\t\\\n+\t\t\t\t  IPEH_TLV_CLASS_FLAG_RTRDSTOPT |\t\\\n+\t\t\t\t  IPEH_TLV_CLASS_FLAG_DSTOPT)\n+\n+#endif /* _UAPI_LINUX_IPEH_H */\ndiff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c\nindex 939d27c..0847d49 100644\n--- a/net/ipv6/exthdrs.c\n+++ b/net/ipv6/exthdrs.c\n@@ -47,6 +47,7 @@\n #ifdef CONFIG_IPV6_SEG6_HMAC\n #include <net/seg6_hmac.h>\n #endif\n+#include <uapi/linux/ipeh.h>\n \n #include <linux/uaccess.h>\n \n@@ -131,7 +132,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)\n \tdstbuf = opt->dst1;\n #endif\n \n-\tif (ipeh_parse_tlv(tlvprocdestopt_lst, skb,\n+\tif (ipeh_parse_tlv(IPEH_TLV_CLASS_FLAG_DSTOPT,\n+\t\t\t   &ipv6_tlv_param_table, skb,\n \t\t\t   init_net.ipv6.sysctl.max_dst_opts_cnt,\n \t\t\t   2, extlen - 2, ipv6_parse_error)) {\n \t\tskb->transport_header += extlen;\n@@ -514,8 +516,13 @@ int __init ipv6_exthdrs_init(void)\n \tif (ret)\n \t\tgoto out_destopt;\n \n+\tret = ipv6_exthdrs_options_init();\n+\tif (ret)\n+\t\tgoto out_nodata;\n out:\n \treturn ret;\n+out_nodata:\n+\tinet6_del_protocol(&nodata_protocol, IPPROTO_NONE);\n out_destopt:\n \tinet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);\n out_rthdr:\n@@ -525,6 +532,7 @@ int __init ipv6_exthdrs_init(void)\n \n void ipv6_exthdrs_exit(void)\n {\n+\tipv6_exthdrs_options_exit();\n \tinet6_del_protocol(&nodata_protocol, IPPROTO_NONE);\n \tinet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);\n \tinet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);\n@@ -555,8 +563,8 @@ int ipv6_parse_hopopts(struct sk_buff *skb)\n \t\tgoto fail_and_free;\n \n \topt->flags |= IP6SKB_HOPBYHOP;\n-\tif (ipeh_parse_tlv(tlvprochopopt_lst, skb,\n-\t\t\t   init_net.ipv6.sysctl.max_hbh_opts_cnt,\n+\tif (ipeh_parse_tlv(IPEH_TLV_CLASS_FLAG_HOPOPT, &ipv6_tlv_param_table,\n+\t\t\t   skb, init_net.ipv6.sysctl.max_hbh_opts_cnt,\n \t\t\t   2, extlen - 2, ipv6_parse_error)) {\n \t\tskb->transport_header += extlen;\n \t\topt = IP6CB(skb);\ndiff --git a/net/ipv6/exthdrs_common.c b/net/ipv6/exthdrs_common.c\nindex 99a0911..cc8db9e 100644\n--- a/net/ipv6/exthdrs_common.c\n+++ b/net/ipv6/exthdrs_common.c\n@@ -150,13 +150,14 @@ EXPORT_SYMBOL_GPL(ipeh_fixup_options);\n  *   - off is offset from skb_transport_header where first TLV is\n  *   - len is length of TLV block\n  */\n-bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n-\t\t    int max_count, int off, int len,\n+bool ipeh_parse_tlv(unsigned int class,\n+\t\t    struct tlv_param_table *tlv_param_table,\n+\t\t    struct sk_buff *skb, int max_count, int off, int len,\n \t\t    bool (*parse_error)(struct sk_buff *skb,\n \t\t\t\t\tint off, enum ipeh_parse_errors error))\n {\n \tconst unsigned char *nh = skb_network_header(skb);\n-\tconst struct tlvtype_proc *curr;\n+\tconst struct tlv_proc *curr;\n \tbool disallow_unknowns = false;\n \tint tlv_count = 0;\n \tint padlen = 0;\n@@ -168,8 +169,10 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n \n \tif (skb_transport_offset(skb) + off + len > skb_headlen(skb)) {\n \t\tif (!parse_error(skb, skb_transport_offset(skb),\n-\t\t\t\t IPEH_PARSE_ERR_EH_TOOBIG))\n-\t\t\tgoto bad;\n+\t\t\t\t IPEH_PARSE_ERR_EH_TOOBIG)) {\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn false;\n+\t\t}\n \n \t\tlen = skb_headlen(skb) - skb_transport_offset(skb) - off;\n \t}\n@@ -177,6 +180,8 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n \t/* ops function based offset on network header */\n \toff += skb_network_header_len(skb);\n \n+\trcu_read_lock();\n+\n \twhile (len > 0) {\n \t\tint optlen = nh[off + 1] + 2;\n \t\tint i;\n@@ -221,26 +226,22 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n \n \t\t\ttlv_count++;\n \t\t\tif (tlv_count > max_count &&\n-\t\t\t    parse_error(skb, off, IPEH_PARSE_ERR_OPT_TOOMANY))\n+\t\t\t    !parse_error(skb, off, IPEH_PARSE_ERR_OPT_TOOMANY))\n \t\t\t\tgoto bad;\n \n-\t\t\tfor (curr = procs; curr->type >= 0; curr++) {\n-\t\t\t\tif (curr->type == nh[off]) {\n-\t\t\t\t\t/* type specific length/alignment\n-\t\t\t\t\t * checks will be performed in the\n-\t\t\t\t\t * func().\n-\t\t\t\t\t */\n-\t\t\t\t\tif (curr->func(skb, off) == false)\n-\t\t\t\t\t\treturn false;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif (curr->type < 0 &&\n-\t\t\t    !parse_error(skb, off,\n+\t\t\tcurr = ipeh_tlv_get_proc(tlv_param_table, &nh[off]);\n+\t\t\tif ((curr->params.r.class & class) && curr->ops.func) {\n+\t\t\t\t/* Handler will apply additional checks to\n+\t\t\t\t * the TLV\n+\t\t\t\t */\n+\t\t\t\tif (!curr->ops.func(class, skb, off))\n+\t\t\t\t\treturn false;\n+\t\t\t} else if (!parse_error(skb, off,\n \t\t\t\t\t disallow_unknowns ?\n \t\t\t\t\t\tIPEH_PARSE_ERR_OPT_UNK_DISALW :\n-\t\t\t\t\t\tIPEH_PARSE_ERR_OPT_UNK))\n+\t\t\t\t\t\tIPEH_PARSE_ERR_OPT_UNK)) {\n \t\t\t\tgoto bad;\n+\t\t\t}\n \n \t\t\tpadlen = 0;\n \t\t\tbreak;\n@@ -249,10 +250,238 @@ bool ipeh_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb,\n \t\tlen -= optlen;\n \t}\n \n-\tif (len == 0)\n+\tif (len == 0) {\n+\t\trcu_read_unlock();\n \t\treturn true;\n+\t}\n bad:\n+\trcu_read_unlock();\n \tkfree_skb(skb);\n \treturn false;\n }\n EXPORT_SYMBOL(ipeh_parse_tlv);\n+\n+/* TLV parameter table functions and structures */\n+\n+/* Default (unset) values for TLV parameters */\n+static const struct tlv_proc tlv_default_proc = {\n+};\n+\n+static DEFINE_MUTEX(tlv_mutex);\n+\n+static size_t tlv_param_table_size(unsigned char count)\n+{\n+\treturn sizeof(struct tlv_param_table_data) +\n+\t    (count * sizeof(struct tlv_type));\n+}\n+\n+static void tlv_param_table_release(struct rcu_head *rcu)\n+{\n+\tstruct tlv_param_table_data *tpt =\n+\t    container_of(rcu, struct tlv_param_table_data, rcu);\n+\n+\tkvfree(tpt);\n+}\n+\n+/* mutex held */\n+static int __tlv_set_one(struct tlv_param_table *tlv_param_table,\n+\t\t\t unsigned char type, const struct tlv_params *params,\n+\t\t\t const struct tlv_ops *ops)\n+{\n+\tstruct tlv_param_table_data *tpt, *told;\n+\tstruct tlv_type *ttype;\n+\n+\ttold = rcu_dereference_protected(tlv_param_table->data,\n+\t\t\t\t\t lockdep_is_held(&tlv_mutex));\n+\n+\t/* Create new TLV table. If there is no exsiting entry then we are\n+\t * adding a new one to the table, else we're modifying an entry.\n+\t */\n+\ttpt = kvmalloc(tlv_param_table_size(told->count + !told->entries[type]),\n+\t\t       GFP_KERNEL);\n+\tif (!tpt)\n+\t\treturn -ENOMEM;\n+\n+\tmemcpy(tpt, told, tlv_param_table_size(told->count));\n+\n+\tif (!told->entries[type]) {\n+\t\tmemset(&tpt->types[told->count], 0, sizeof(struct tlv_type));\n+\t\ttpt->entries[type] = told->count;\n+\t\ttpt->count = told->count + 1;\n+\t}\n+\n+\tttype = &tpt->types[tpt->entries[type]];\n+\n+\tttype->proc.params = *params;\n+\tttype->proc.ops = ops ? *ops : tlv_default_proc.ops;\n+\n+\trcu_assign_pointer(tlv_param_table->data, tpt);\n+\tcall_rcu(&told->rcu, tlv_param_table_release);\n+\n+\treturn 0;\n+}\n+\n+int __ipeh_tlv_set(struct tlv_param_table *tlv_param_table, unsigned char type,\n+\t\t   const struct tlv_params *params, const struct tlv_ops *ops)\n+{\n+\tint retv;\n+\n+\tif (type < 2)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(&tlv_mutex);\n+\tretv = __tlv_set_one(tlv_param_table, type, params, ops);\n+\tmutex_unlock(&tlv_mutex);\n+\n+\treturn retv;\n+}\n+EXPORT_SYMBOL(__ipeh_tlv_set);\n+\n+/* mutex held */\n+static int __tlv_unset_one(struct tlv_param_table *tlv_param_table,\n+\t\t\t   unsigned char type)\n+{\n+\tstruct tlv_param_table_data *tpt, *told;\n+\tunsigned int i, pos;\n+\n+\ttold = rcu_dereference_protected(tlv_param_table->data,\n+\t\t\t\t\t lockdep_is_held(&tlv_mutex));\n+\n+\tif (!told->entries[type])\n+\t\treturn 0;\n+\n+\ttpt = kvmalloc(tlv_param_table_size(told->count - 1),\n+\t\t       GFP_KERNEL);\n+\tif (!tpt)\n+\t\treturn -ENOMEM;\n+\n+\tpos = told->entries[type];\n+\n+\tmemcpy(tpt->types, told->types, pos * sizeof(struct tlv_type));\n+\tmemcpy(&tpt->types[pos], &told->types[pos + 1],\n+\t       (told->count - pos - 1) * sizeof(struct tlv_type));\n+\n+\tfor (i = 0; i < 256; i++) {\n+\t\tif (told->entries[i] > pos)\n+\t\t\ttpt->entries[i] = told->entries[i] - 1;\n+\t\telse\n+\t\t\ttpt->entries[i] = told->entries[i];\n+\t}\n+\n+\t/* Clear entry for type being unset (point to default params) */\n+\ttpt->entries[type] = 0;\n+\n+\ttpt->count = told->count - 1;\n+\n+\trcu_assign_pointer(tlv_param_table->data, tpt);\n+\tcall_rcu(&told->rcu, tlv_param_table_release);\n+\n+\treturn 0;\n+}\n+\n+/* tlv_internal_proc_type is used to check it the TLV proc was set\n+ * internally. This is deduced by checking if any operations are defined.\n+ */\n+static bool tlv_internal_proc_type(struct tlv_proc *proc)\n+{\n+\treturn !!proc->ops.func;\n+}\n+\n+int __ipeh_tlv_unset(struct tlv_param_table *tlv_param_table,\n+\t\t     unsigned char type, bool params_only)\n+{\n+\tstruct tlv_proc *tproc;\n+\tint retv;\n+\n+\tif (type < 2)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(&tlv_mutex);\n+\n+\ttproc = ipeh_tlv_get_proc_by_type(tlv_param_table, type);\n+\n+\tif (params_only && tlv_internal_proc_type(tproc)) {\n+\t\t/* TLV was set by internal source, so maintain the\n+\t\t * non-parameter fields (i.e. the operations).\n+\t\t */\n+\t\tretv = __tlv_set_one(tlv_param_table, type,\n+\t\t\t\t     &tlv_default_proc.params,\n+\t\t\t\t     &tproc->ops);\n+\t} else {\n+\t\tretv = __tlv_unset_one(tlv_param_table, type);\n+\t}\n+\n+\tmutex_unlock(&tlv_mutex);\n+\n+\treturn retv;\n+}\n+EXPORT_SYMBOL(__ipeh_tlv_unset);\n+\n+int ipeh_exthdrs_init(struct tlv_param_table *tlv_param_table,\n+\t\t      const struct tlv_proc_init *tlv_init_params,\n+\t\t      int num_init_params)\n+{\n+\tstruct tlv_param_table_data *tpt;\n+\tint pos = 0, i;\n+\tsize_t tsize;\n+\n+\ttsize = tlv_param_table_size(num_init_params + 1);\n+\n+\ttpt = kvmalloc(tsize, GFP_KERNEL);\n+\tif (!tpt)\n+\t\treturn -ENOMEM;\n+\n+\tmemset(tpt, 0, tsize);\n+\n+\t/* Zeroth TLV proc entry is default */\n+\ttpt->types[pos++].proc = tlv_default_proc;\n+\n+\tfor (i = 0; i < num_init_params; i++, pos++) {\n+\t\tconst struct tlv_proc_init *tpi = &tlv_init_params[i];\n+\n+\t\tif (WARN_ON(tpi->type < 2)) {\n+\t\t\t /* Padding TLV initialized? */\n+\t\t\tgoto err_inval;\n+\t\t}\n+\t\tif (WARN_ON(tpt->entries[tpi->type])) {\n+\t\t\t/* TLV type already set */\n+\t\t\tgoto err_inval;\n+\t\t}\n+\n+\t\ttpt->types[pos].proc = tpi->proc;\n+\t\ttpt->entries[tpi->type] = pos;\n+\t}\n+\n+\ttpt->count = pos;\n+\n+\tRCU_INIT_POINTER(tlv_param_table->data, tpt);\n+\n+\treturn 0;\n+\n+err_inval:\n+\tkvfree(tpt);\n+\treturn -EINVAL;\n+}\n+EXPORT_SYMBOL(ipeh_exthdrs_init);\n+\n+static void tlv_destroy_param_table(struct tlv_param_table *tlv_param_table)\n+{\n+\tstruct tlv_param_table_data *tpt;\n+\n+\tmutex_lock(&tlv_mutex);\n+\n+\ttpt = rcu_dereference_protected(tlv_param_table->data,\n+\t\t\t\t\tlockdep_is_held(&tlv_mutex));\n+\tif (tpt) {\n+\t\trcu_assign_pointer(tlv_param_table->data, NULL);\n+\t\tcall_rcu(&tpt->rcu, tlv_param_table_release);\n+\t}\n+\n+\tmutex_unlock(&tlv_mutex);\n+}\n+\n+void ipeh_exthdrs_fini(struct tlv_param_table *tlv_param_table)\n+{\n+\ttlv_destroy_param_table(tlv_param_table);\n+}\n+EXPORT_SYMBOL(ipeh_exthdrs_fini);\ndiff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c\nindex 032e072..d4b373e 100644\n--- a/net/ipv6/exthdrs_options.c\n+++ b/net/ipv6/exthdrs_options.c\n@@ -11,11 +11,12 @@\n #if IS_ENABLED(CONFIG_IPV6_MIP6)\n #include <net/xfrm.h>\n #endif\n+#include <uapi/linux/ipeh.h>\n \n /* Destination options header */\n \n #if IS_ENABLED(CONFIG_IPV6_MIP6)\n-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)\n+static bool ipv6_dest_hao(unsigned int class, struct sk_buff *skb, int optoff)\n {\n \tstruct ipv6_destopt_hao *hao;\n \tstruct inet6_skb_parm *opt = IP6CB(skb);\n@@ -74,16 +75,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)\n }\n #endif\n \n-const struct tlvtype_proc tlvprocdestopt_lst[] = {\n-#if IS_ENABLED(CONFIG_IPV6_MIP6)\n-\t{\n-\t\t.type\t= IPV6_TLV_HAO,\n-\t\t.func\t= ipv6_dest_hao,\n-\t},\n-#endif\n-\t{-1,\t\t\tNULL}\n-};\n-\n /* Hop-by-hop options */\n \n /* Note: we cannot rely on skb_dst(skb) before we assign it in\n@@ -102,7 +93,7 @@ static inline struct net *ipv6_skb_net(struct sk_buff *skb)\n \n /* Router Alert as of RFC 2711 */\n \n-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)\n+static bool ipv6_hop_ra(unsigned int class, struct sk_buff *skb, int optoff)\n {\n \tconst unsigned char *nh = skb_network_header(skb);\n \n@@ -120,7 +111,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)\n \n /* Jumbo payload */\n \n-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)\n+static bool ipv6_hop_jumbo(unsigned int class, struct sk_buff *skb, int optoff)\n {\n \tconst unsigned char *nh = skb_network_header(skb);\n \tstruct inet6_dev *idev = __in6_dev_get_safely(skb->dev);\n@@ -164,7 +155,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)\n \n /* CALIPSO RFC 5570 */\n \n-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)\n+static bool ipv6_hop_calipso(unsigned int class, struct sk_buff *skb,\n+\t\t\t     int optoff)\n {\n \tconst unsigned char *nh = skb_network_header(skb);\n \n@@ -184,18 +176,45 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)\n \treturn false;\n }\n \n-const struct tlvtype_proc tlvprochopopt_lst[] = {\n+static const struct tlv_proc_init tlv_ipv6_init_params[] __initconst = {\n+#if IS_ENABLED(CONFIG_IPV6_MIP6)\n \t{\n-\t\t.type\t= IPV6_TLV_ROUTERALERT,\n-\t\t.func\t= ipv6_hop_ra,\n+\t\t.type = IPV6_TLV_HAO,\n+\n+\t\t.proc.ops.func = ipv6_dest_hao,\n+\t\t.proc.params.r.class = IPEH_TLV_CLASS_FLAG_DSTOPT,\n \t},\n+#endif\n \t{\n-\t\t.type\t= IPV6_TLV_JUMBO,\n-\t\t.func\t= ipv6_hop_jumbo,\n+\t\t.type = IPV6_TLV_ROUTERALERT,\n+\n+\t\t.proc.ops.func = ipv6_hop_ra,\n+\t\t.proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,\n \t},\n \t{\n-\t\t.type\t= IPV6_TLV_CALIPSO,\n-\t\t.func\t= ipv6_hop_calipso,\n+\t\t.type = IPV6_TLV_JUMBO,\n+\n+\t\t.proc.ops.func\t= ipv6_hop_jumbo,\n+\t\t.proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,\n+\t},\n+\t{\n+\t\t.type = IPV6_TLV_CALIPSO,\n+\n+\t\t.proc.ops.func = ipv6_hop_calipso,\n+\t\t.proc.params.r.class = IPEH_TLV_CLASS_FLAG_HOPOPT,\n \t},\n-\t{ -1, }\n };\n+\n+struct tlv_param_table __rcu ipv6_tlv_param_table;\n+EXPORT_SYMBOL(ipv6_tlv_param_table);\n+\n+int __init ipv6_exthdrs_options_init(void)\n+{\n+\treturn ipeh_exthdrs_init(&ipv6_tlv_param_table, tlv_ipv6_init_params,\n+\t\t\t\t ARRAY_SIZE(tlv_ipv6_init_params));\n+}\n+\n+void ipv6_exthdrs_options_exit(void)\n+{\n+\tipeh_exthdrs_fini(&ipv6_tlv_param_table);\n+}\n",
    "prefixes": [
        "net-next",
        "4/7"
    ]
}