From patchwork Wed Jun 24 19:23:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Iurman X-Patchwork-Id: 217164 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6099EC433DF for ; Wed, 24 Jun 2020 19:33:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 20D9420823 for ; Wed, 24 Jun 2020 19:33:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=uliege.be header.i=@uliege.be header.b="cKGM/YTY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2406135AbgFXTdS (ORCPT ); Wed, 24 Jun 2020 15:33:18 -0400 Received: from serv108.segi.ulg.ac.be ([139.165.32.111]:46269 "EHLO serv108.segi.ulg.ac.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2405904AbgFXTdR (ORCPT ); Wed, 24 Jun 2020 15:33:17 -0400 Received: from ubuntu18.lan (unknown [109.129.49.166]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by serv108.segi.ulg.ac.be (Postfix) with ESMTPSA id DE42E200CCF8; Wed, 24 Jun 2020 21:24:19 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 serv108.segi.ulg.ac.be DE42E200CCF8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uliege.be; s=ulg20190529; t=1593026664; bh=3ttSCW24Ac7KxX8kNXe+Zh/EmN2B2JNR9ck1QkeyBYQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cKGM/YTY/PaeHsXMuVvEFQUIGNhNABRvlWZynJTbA/c89VlLh+TtPKozpeSUSnMOv Ra21H4rToY0cMNZ7AqoeY22olhIQe8YK95/ZFcMFv0uacBoEdzfDj9bKZ66l1hnbwl R32QIUw49Yb4+M1wvCWsTc/7ugeXTJVXUeXmU61VY3M5DQChb/sz2dtCCEm/6arzeJ ssNihrust7NeUmBOxbotOd8w+Rg0bZVa+5P9h9gQa/rWWw8HzFNr0SZsGeM03j3eOu UO7p4PFFohiIez4aa5obhWRzDiaOiXwUGH/wW27NIwqh1G54/NlPbdQurrr2Jl8bJo PDKc7KdutSN/g== From: Justin Iurman To: netdev@vger.kernel.org Cc: davem@davemloft.net, justin.iurman@uliege.be Subject: [PATCH net-next 1/5] ipv6: eh: Introduce removable TLVs Date: Wed, 24 Jun 2020 21:23:06 +0200 Message-Id: <20200624192310.16923-2-justin.iurman@uliege.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200624192310.16923-1-justin.iurman@uliege.be> References: <20200624192310.16923-1-justin.iurman@uliege.be> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add the possibility to remove one or more consecutive TLVs without messing up the alignment of others. For now, only IOAM requires this behavior. By default, an 8-octet boundary is automatically assumed. This is the price to pay (at most a useless 4-octet padding) to make sure everything is still aligned after the removal. Proof: let's assume for instance the following alignments 2n, 4n and 8n respectively for options X, Y and Z, inside a Hop-by-Hop extension header. Example 1: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next header | Hdr Ext Len | X | X | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | X | X | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | ~ Option to be removed (8 octets) ~ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Y | Y | Y | Y | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Padding | Padding | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Result 1: assuming a 4-octet boundary would work, as well as an 8-octet boundary (same result in both cases). +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next header | Hdr Ext Len | X | X | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | X | X | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Y | Y | Y | Y | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Padding | Padding | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Example 2: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next header | Hdr Ext Len | X | X | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | X | X | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option to be removed (4 octets) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Y | Y | Y | Y | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Result 2: assuming a 4-octet boundary WOULD NOT WORK. Indeed, option Z would not be 8n-aligned and the Hop-by-Hop size would not be a multiple of 8 anymore. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next header | Hdr Ext Len | X | X | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | X | X | Padding | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Y | Y | Y | Y | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Z | Z | Z | Z | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Therefore, the largest (8-octet) boundary is assumed by default and for all, which means that blocks are only moved in multiples of 8. This assertion guarantees good alignment. Signed-off-by: Justin Iurman --- net/ipv6/exthdrs.c | 134 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 26 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index e9b366994475..f27ab3bf2e0c 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -52,17 +52,27 @@ #include -/* - * Parsing tlv encoded headers. +/* States for TLV parsing functions. */ + +enum { + TLV_ACCEPT, + TLV_REJECT, + TLV_REMOVE, + __TLV_MAX +}; + +/* Parsing TLV encoded headers. * - * Parsing function "func" returns true, if parsing succeed - * and false, if it failed. - * It MUST NOT touch skb->h. + * Parsing function "func" returns either: + * - TLV_ACCEPT if parsing succeeds + * - TLV_REJECT if parsing fails + * - TLV_REMOVE if TLV must be removed + * It MUST NOT touch skb->h. */ struct tlvtype_proc { int type; - bool (*func)(struct sk_buff *skb, int offset); + int (*func)(struct sk_buff *skb, int offset); }; /********************* @@ -109,19 +119,67 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, return false; } +/* Remove one or several consecutive TLVs and recompute offsets, lengths */ + +static int remove_tlv(int start, int end, struct sk_buff *skb) +{ + int len = end - start; + int padlen = len % 8; + unsigned char *h; + int rlen, off; + u16 pl_len; + + rlen = len - padlen; + if (rlen) { + skb_pull(skb, rlen); + memmove(skb_network_header(skb) + rlen, skb_network_header(skb), + start); + skb_postpull_rcsum(skb, skb_network_header(skb), rlen); + + skb_reset_network_header(skb); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + pl_len = be16_to_cpu(ipv6_hdr(skb)->payload_len) - rlen; + ipv6_hdr(skb)->payload_len = cpu_to_be16(pl_len); + + skb_transport_header(skb)[1] -= rlen >> 3; + end -= rlen; + } + + if (padlen) { + off = end - padlen; + h = skb_network_header(skb); + + if (padlen == 1) { + h[off] = IPV6_TLV_PAD1; + } else { + padlen -= 2; + + h[off] = IPV6_TLV_PADN; + h[off + 1] = padlen; + memset(&h[off + 2], 0, padlen); + } + } + + return end; +} + /* Parse tlv encoded option header (hop-by-hop or destination) */ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb, - int max_count) + int max_count, + bool removable) { int len = (skb_transport_header(skb)[1] + 1) << 3; - const unsigned char *nh = skb_network_header(skb); + unsigned char *nh = skb_network_header(skb); int off = skb_network_header_len(skb); const struct tlvtype_proc *curr; bool disallow_unknowns = false; + int off_remove = 0; int tlv_count = 0; int padlen = 0; + int ret; if (unlikely(max_count < 0)) { disallow_unknowns = true; @@ -173,12 +231,14 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, if (tlv_count > max_count) goto bad; + ret = -1; 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) + ret = curr->func(skb, off); + if (ret == TLV_REJECT) return false; break; } @@ -187,6 +247,17 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) return false; + if (removable) { + if (ret == TLV_REMOVE) { + if (!off_remove) + off_remove = off - padlen; + } else if (off_remove) { + off = remove_tlv(off_remove, off, skb); + nh = skb_network_header(skb); + off_remove = 0; + } + } + padlen = 0; break; } @@ -194,8 +265,13 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, len -= optlen; } - if (len == 0) + if (len == 0) { + /* Don't forget last TLV if it must be removed */ + if (off_remove) + remove_tlv(off_remove, off, skb); + return true; + } bad: kfree_skb(skb); return false; @@ -206,7 +282,7 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, *****************************/ #if IS_ENABLED(CONFIG_IPV6_MIP6) -static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) +static int ipv6_dest_hao(struct sk_buff *skb, int optoff) { struct ipv6_destopt_hao *hao; struct inet6_skb_parm *opt = IP6CB(skb); @@ -257,11 +333,11 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) if (skb->tstamp == 0) __net_timestamp(skb); - return true; + return TLV_ACCEPT; discard: kfree_skb(skb); - return false; + return TLV_REJECT; } #endif @@ -306,7 +382,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) #endif if (ip6_parse_tlv(tlvprocdestopt_lst, skb, - init_net.ipv6.sysctl.max_dst_opts_cnt)) { + init_net.ipv6.sysctl.max_dst_opts_cnt, + false)) { skb->transport_header += extlen; opt = IP6CB(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) @@ -918,24 +995,24 @@ 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 int 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; + return TLV_ACCEPT; } net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n", nh[optoff + 1]); kfree_skb(skb); - return false; + return TLV_REJECT; } /* Jumbo payload */ -static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) +static int 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); @@ -953,12 +1030,12 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) if (pkt_len <= IPV6_MAXPLEN) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); - return false; + return TLV_REJECT; } if (ipv6_hdr(skb)->payload_len) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); - return false; + return TLV_REJECT; } if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { @@ -970,16 +1047,16 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) goto drop; IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM; - return true; + return TLV_ACCEPT; drop: kfree_skb(skb); - return false; + return TLV_REJECT; } /* CALIPSO RFC 5570 */ -static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) +static int ipv6_hop_calipso(struct sk_buff *skb, int optoff) { const unsigned char *nh = skb_network_header(skb); @@ -992,11 +1069,11 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) if (!calipso_validate(skb, nh + optoff)) goto drop; - return true; + return TLV_ACCEPT; drop: kfree_skb(skb); - return false; + return TLV_REJECT; } static const struct tlvtype_proc tlvprochopopt_lst[] = { @@ -1041,7 +1118,12 @@ int ipv6_parse_hopopts(struct sk_buff *skb) opt->flags |= IP6SKB_HOPBYHOP; if (ip6_parse_tlv(tlvprochopopt_lst, skb, - init_net.ipv6.sysctl.max_hbh_opts_cnt)) { + init_net.ipv6.sysctl.max_hbh_opts_cnt, + true)) { + /* we need to refresh the length in case + * at least one TLV was removed + */ + extlen = (skb_transport_header(skb)[1] + 1) << 3; skb->transport_header += extlen; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr); From patchwork Wed Jun 24 19:23:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Iurman X-Patchwork-Id: 217165 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73E3DC433E1 for ; Wed, 24 Jun 2020 19:33:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 576D820823 for ; Wed, 24 Jun 2020 19:33:22 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=uliege.be header.i=@uliege.be header.b="LOVk2/qZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2406194AbgFXTdU (ORCPT ); Wed, 24 Jun 2020 15:33:20 -0400 Received: from serv108.segi.ulg.ac.be ([139.165.32.111]:46267 "EHLO serv108.segi.ulg.ac.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2405864AbgFXTdR (ORCPT ); Wed, 24 Jun 2020 15:33:17 -0400 Received: from ubuntu18.lan (unknown [109.129.49.166]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by serv108.segi.ulg.ac.be (Postfix) with ESMTPSA id 29935200CCFD; Wed, 24 Jun 2020 21:24:25 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 serv108.segi.ulg.ac.be 29935200CCFD DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uliege.be; s=ulg20190529; t=1593026665; bh=AG9GC5e8CFukOwFrUbH01AtmaswJf1IlCmK91l3bF1Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LOVk2/qZ72Bosq0Vf7Hg2iVRol56Dq3r2Bgbck5nPx8v7NcxwHCgJyrZdsmPjWuWJ 4/Xw+xWXe1r9EKpfpMAbkgWBonXr4nlkPrq9Vxli1goLz/vF0YuB/AwF5dPc+iMP6w X5O6DfIpFuuUfmzFuer4TI7i0PwiNxsXiVue4nlQengpGxtnXyl3EMwH3kL+VsIAqB wTlLSTVtkXxki4Ntdz8vGj9GaXnwYuRtYjyiMRc0sFxoCbrJqiDmHOVvQMXFfM8pTy EtAttwmNHQyzdcmGCMYEO/v4rrBAwP+1D7Uw8Cj5k11rIyQUPwDNINIm17OJ7qe41e N6WXLJM6Uw5OA== From: Justin Iurman To: netdev@vger.kernel.org Cc: davem@davemloft.net, justin.iurman@uliege.be Subject: [PATCH net-next 2/5] ipv6: IOAM tunnel decapsulation Date: Wed, 24 Jun 2020 21:23:07 +0200 Message-Id: <20200624192310.16923-3-justin.iurman@uliege.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200624192310.16923-1-justin.iurman@uliege.be> References: <20200624192310.16923-1-justin.iurman@uliege.be> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement the IOAM egress behavior. According to RFC 8200: "Extension headers (except for the Hop-by-Hop Options header) are not processed, inserted, or deleted by any node along a packet's delivery path, until the packet reaches the node (or each of the set of nodes, in the case of multicast) identified in the Destination Address field of the IPv6 header." Therefore, an ingress node (an IOAM domain border) must encapsulate an incoming IPv6 packet with another similar IPv6 header that will contain IOAM data while it traverses the domain. When leaving, the egress node, another IOAM domain border which is also the tunnel destination, must decapsulate the packet. Signed-off-by: Justin Iurman --- include/linux/ipv6.h | 1 + net/ipv6/ip6_input.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 2cb445a8fc9e..5312a718bc7a 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -138,6 +138,7 @@ struct inet6_skb_parm { #define IP6SKB_HOPBYHOP 32 #define IP6SKB_L3SLAVE 64 #define IP6SKB_JUMBOGRAM 128 +#define IP6SKB_IOAM 256 }; #if defined(CONFIG_NET_L3_MASTER_DEV) diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index e96304d8a4a7..8cf75cc5e806 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -361,9 +361,11 @@ INDIRECT_CALLABLE_DECLARE(int tcp_v6_rcv(struct sk_buff *)); void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, bool have_final) { + struct inet6_skb_parm *opt = IP6CB(skb); const struct inet6_protocol *ipprot; struct inet6_dev *idev; unsigned int nhoff; + u8 hop_limit; bool raw; /* @@ -450,6 +452,25 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, } else { if (!raw) { if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { + /* IOAM Tunnel Decapsulation + * Packet is going to re-enter the stack + */ + if (nexthdr == NEXTHDR_IPV6 && + (opt->flags & IP6SKB_IOAM)) { + hop_limit = ipv6_hdr(skb)->hop_limit; + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->encapsulation = 0; + + ipv6_hdr(skb)->hop_limit = hop_limit; + __skb_tunnel_rx(skb, skb->dev, + dev_net(skb->dev)); + + netif_rx(skb); + goto out; + } + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INUNKNOWNPROTOS); icmpv6_send(skb, ICMPV6_PARAMPROB, @@ -461,6 +482,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, consume_skb(skb); } } +out: return; discard: From patchwork Wed Jun 24 19:23:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Iurman X-Patchwork-Id: 217163 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D71E6C433E0 for ; Wed, 24 Jun 2020 19:33:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A31DC20823 for ; Wed, 24 Jun 2020 19:33:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=uliege.be header.i=@uliege.be header.b="efA0m9sd" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2406227AbgFXTdf (ORCPT ); Wed, 24 Jun 2020 15:33:35 -0400 Received: from serv108.segi.ulg.ac.be ([139.165.32.111]:46310 "EHLO serv108.segi.ulg.ac.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2406142AbgFXTdf (ORCPT ); Wed, 24 Jun 2020 15:33:35 -0400 Received: from ubuntu18.lan (unknown [109.129.49.166]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by serv108.segi.ulg.ac.be (Postfix) with ESMTPSA id B26F1200CD0C; Wed, 24 Jun 2020 21:24:25 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 serv108.segi.ulg.ac.be B26F1200CD0C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uliege.be; s=ulg20190529; t=1593026665; bh=7/8yK5qvyAR/dS4O9L+Ejjm9LBqSj6ebNOZjo2vRzM8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=efA0m9sdfVp5MzNh34CNdn42MPOapOg/PsmUB4DOkz/Ze6PFf5jPKDx6bFi5w152L 4Q7Sr5rSEd3M96zGBdWSaVAgxj/vJqolhjKcgSeR9dNsaxf40zG/fn0abU6D1NQwji z/s3YlS1S8KgFK222MJPLL573wBNlpZQS4jXrv14lJKPxiYT20wK58eGNgDCzUQ7i2 iHqlG0bu7pjPbJmTeG9tyq5a+Ss7Bm6/V0Zs5J1fUO9qo3hhYr1JALvZqGIEvx8RlP s7CG4ETQlaf3ACqqBRuM1JvwBX8nZ52FjDaA7c9pHwUBFhIJ4QSeww1g61qr+uq26l uSc3FoIC5AHAQ== From: Justin Iurman To: netdev@vger.kernel.org Cc: davem@davemloft.net, justin.iurman@uliege.be Subject: [PATCH net-next 4/5] ipv6: ioam: Generic Netlink to configure IOAM Date: Wed, 24 Jun 2020 21:23:09 +0200 Message-Id: <20200624192310.16923-5-justin.iurman@uliege.be> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200624192310.16923-1-justin.iurman@uliege.be> References: <20200624192310.16923-1-justin.iurman@uliege.be> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add Generic Netlink commands to allow userspace to configure IOAM namespaces and schemas. The target is iproute2 and the patch is ready. It will be posted as soon as this patchset is merged. Here is a taste: $ sudo ip ioam Usage: ip ioam { namespace | schema } { show | del ID } schema add ID DATA namespace add ID [ DATA ] [ POP ] namespace set ID schema { ID | none } POP := { true | false } Signed-off-by: Justin Iurman --- include/linux/ioam6.h | 7 + include/uapi/linux/ioam6.h | 43 +++ net/ipv6/ioam6.c | 519 ++++++++++++++++++++++++++++++++++++- 3 files changed, 566 insertions(+), 3 deletions(-) create mode 100644 include/linux/ioam6.h create mode 100644 include/uapi/linux/ioam6.h diff --git a/include/linux/ioam6.h b/include/linux/ioam6.h new file mode 100644 index 000000000000..156223095e57 --- /dev/null +++ b/include/linux/ioam6.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_IOAM6_H +#define _LINUX_IOAM6_H + +#include + +#endif diff --git a/include/uapi/linux/ioam6.h b/include/uapi/linux/ioam6.h new file mode 100644 index 000000000000..d2be5f820dc5 --- /dev/null +++ b/include/uapi/linux/ioam6.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_IOAM6_H +#define _UAPI_LINUX_IOAM6_H + +#define IOAM6_GENL_NAME "IOAM6" +#define IOAM6_GENL_VERSION 0x1 + +enum { + IOAM6_ATTR_UNSPEC, + + IOAM6_ATTR_NS_ID, /* u16 */ + IOAM6_ATTR_NS_DATA, /* u64 */ + IOAM6_ATTR_NS_POP, /* Flag */ + +#define IOAM6_MAX_SCHEMA_DATA_LEN (255 * 4) + IOAM6_ATTR_SC_ID, /* u32 */ + IOAM6_ATTR_SC_DATA, /* Binary */ + IOAM6_ATTR_SC_NONE, /* Flag */ + + IOAM6_ATTR_PAD, + + __IOAM6_ATTR_MAX, +}; +#define IOAM6_ATTR_MAX (__IOAM6_ATTR_MAX - 1) + +enum { + IOAM6_CMD_UNSPEC, + + IOAM6_CMD_ADD_NAMESPACE, + IOAM6_CMD_DEL_NAMESPACE, + IOAM6_CMD_DUMP_NAMESPACES, + + IOAM6_CMD_ADD_SCHEMA, + IOAM6_CMD_DEL_SCHEMA, + IOAM6_CMD_DUMP_SCHEMAS, + + IOAM6_CMD_NS_SET_SCHEMA, + + __IOAM6_CMD_MAX, +}; +#define IOAM6_CMD_MAX (__IOAM6_CMD_MAX - 1) + +#endif diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index 406aa78eb504..e414e915bf1e 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -11,8 +11,10 @@ #include #include #include +#include #include +#include #include static inline void ioam6_ns_release(struct ioam6_namespace *ns) @@ -71,6 +73,507 @@ static const struct rhashtable_params rht_sc_params = { .obj_cmpfn = ioam6_sc_cmpfn, }; +static struct genl_family ioam6_genl_family; + +static const struct nla_policy ioam6_genl_policy[IOAM6_ATTR_MAX + 1] = { + [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 }, + [IOAM6_ATTR_NS_DATA] = { .type = NLA_U64 }, + [IOAM6_ATTR_NS_POP] = { .type = NLA_FLAG }, + [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 }, + [IOAM6_ATTR_SC_DATA] = { .type = NLA_BINARY, + .len = IOAM6_MAX_SCHEMA_DATA_LEN }, + [IOAM6_ATTR_SC_NONE] = { .type = NLA_FLAG }, +}; + +static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct ioam6_pernet_data *nsdata; + struct ioam6_namespace *ns; + __be16 ns_id; + int err; + + if (!info->attrs[IOAM6_ATTR_NS_ID]) + return -EINVAL; + + ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); + nsdata = ioam6_pernet(net); + + mutex_lock(&nsdata->lock); + + ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params); + if (ns) { + err = -EEXIST; + goto out_unlock; + } + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + if (!ns) { + err = -ENOMEM; + goto out_unlock; + } + + ns->id = ns_id; + ns->remove_tlv = info->attrs[IOAM6_ATTR_NS_POP] ? true : false; + + if (!info->attrs[IOAM6_ATTR_NS_DATA]) { + ns->data = cpu_to_be64(IOAM6_EMPTY_FIELD_u64); + } else { + ns->data = cpu_to_be64( + nla_get_u64(info->attrs[IOAM6_ATTR_NS_DATA])); + } + + err = rhashtable_lookup_insert_fast(&nsdata->namespaces, &ns->head, + rht_ns_params); + if (err) + kfree(ns); + +out_unlock: + mutex_unlock(&nsdata->lock); + return err; +} + +static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct ioam6_pernet_data *nsdata; + struct ioam6_namespace *ns; + __be16 ns_id; + int err; + + if (!info->attrs[IOAM6_ATTR_NS_ID]) + return -EINVAL; + + ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); + nsdata = ioam6_pernet(net); + + mutex_lock(&nsdata->lock); + + ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params); + if (!ns) { + err = -ENOENT; + goto out_unlock; + } + + if (ns->schema) + ns->schema->ns = NULL; + + err = rhashtable_remove_fast(&nsdata->namespaces, &ns->head, + rht_ns_params); + if (err) { + ns->schema->ns = ns; + goto out_unlock; + } + + ioam6_ns_release(ns); + +out_unlock: + mutex_unlock(&nsdata->lock); + return err; +} + +static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns, + u32 portid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd) +{ + void *hdr; + u64 data; + + hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd); + if (!hdr) + return -ENOMEM; + + data = be64_to_cpu(ns->data); + + if (nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) || + (data != IOAM6_EMPTY_FIELD_u64 && + nla_put_u64_64bit(skb, IOAM6_ATTR_NS_DATA, data, IOAM6_ATTR_PAD)) || + (ns->remove_tlv && nla_put_flag(skb, IOAM6_ATTR_NS_POP)) || + (ns->schema && nla_put_u32(skb, IOAM6_ATTR_SC_ID, ns->schema->id))) + goto nla_put_failure; + + genlmsg_end(skb, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +static int ioam6_genl_dumpns_start(struct netlink_callback *cb) +{ + struct net *net = sock_net(cb->skb->sk); + struct ioam6_pernet_data *nsdata; + struct rhashtable_iter *iter; + + nsdata = ioam6_pernet(net); + iter = (struct rhashtable_iter *)cb->args[0]; + + if (!iter) { + iter = kmalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + cb->args[0] = (long)iter; + } + + rhashtable_walk_enter(&nsdata->namespaces, iter); + + return 0; +} + +static int ioam6_genl_dumpns_done(struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + + rhashtable_walk_exit(iter); + kfree(iter); + + return 0; +} + +static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + struct ioam6_namespace *ns; + int err; + + rhashtable_walk_start(iter); + + for (;;) { + ns = rhashtable_walk_next(iter); + + if (IS_ERR(ns)) { + if (PTR_ERR(ns) == -EAGAIN) + continue; + err = PTR_ERR(ns); + goto done; + } else if (!ns) { + break; + } + + err = __ioam6_genl_dumpns_element(ns, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI, + skb, + IOAM6_CMD_DUMP_NAMESPACES); + if (err) + goto done; + } + + err = skb->len; + +done: + rhashtable_walk_stop(iter); + return err; +} + +static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct ioam6_pernet_data *nsdata; + struct ioam6_schema *sc; + int len, pad, err; + u32 sc_id; + + if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA]) + return -EINVAL; + + sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); + nsdata = ioam6_pernet(net); + + mutex_lock(&nsdata->lock); + + sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id, rht_sc_params); + if (sc) { + err = -EEXIST; + goto out_unlock; + } + + sc = kzalloc(sizeof(*sc), GFP_KERNEL); + if (!sc) { + err = -ENOMEM; + goto out_unlock; + } + + len = nla_len(info->attrs[IOAM6_ATTR_SC_DATA]); + pad = (4 - (len % 4)) % 4; + + sc->data = kzalloc(len + pad, GFP_KERNEL); + if (!sc->data) { + err = -ENOMEM; + goto free_sc; + } + + sc->id = sc_id; + sc->len = len + pad; + sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24)); + + nla_memcpy(sc->data, info->attrs[IOAM6_ATTR_SC_DATA], len); + + err = rhashtable_lookup_insert_fast(&nsdata->schemas, &sc->head, + rht_sc_params); + if (err) + goto free_data; + +out_unlock: + mutex_unlock(&nsdata->lock); + return err; +free_data: + kfree(sc->data); +free_sc: + kfree(sc); + goto out_unlock; +} + +static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct ioam6_pernet_data *nsdata; + struct ioam6_schema *sc; + u32 sc_id; + int err; + + if (!info->attrs[IOAM6_ATTR_SC_ID]) + return -EINVAL; + + sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); + nsdata = ioam6_pernet(net); + + mutex_lock(&nsdata->lock); + + sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id, rht_sc_params); + if (!sc) { + err = -ENOENT; + goto out_unlock; + } + + if (sc->ns) + sc->ns->schema = NULL; + + err = rhashtable_remove_fast(&nsdata->schemas, &sc->head, + rht_sc_params); + if (err) { + sc->ns->schema = sc; + goto out_unlock; + } + + ioam6_sc_release(sc); + +out_unlock: + mutex_unlock(&nsdata->lock); + return err; +} + +static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc, + u32 portid, u32 seq, u32 flags, + struct sk_buff *skb, u8 cmd) +{ + void *hdr; + + hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd); + if (!hdr) + return -ENOMEM; + + if (nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id) || + nla_put(skb, IOAM6_ATTR_SC_DATA, sc->len, sc->data) || + (sc->ns && nla_put_u16(skb, IOAM6_ATTR_NS_ID, + be16_to_cpu(sc->ns->id)))) + goto nla_put_failure; + + genlmsg_end(skb, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(skb, hdr); + return -EMSGSIZE; +} + +static int ioam6_genl_dumpsc_start(struct netlink_callback *cb) +{ + struct net *net = sock_net(cb->skb->sk); + struct ioam6_pernet_data *nsdata; + struct rhashtable_iter *iter; + + nsdata = ioam6_pernet(net); + iter = (struct rhashtable_iter *)cb->args[0]; + + if (!iter) { + iter = kmalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + cb->args[0] = (long)iter; + } + + rhashtable_walk_enter(&nsdata->schemas, iter); + + return 0; +} + +static int ioam6_genl_dumpsc_done(struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + + rhashtable_walk_exit(iter); + kfree(iter); + + return 0; +} + +static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; + struct ioam6_schema *sc; + int err; + + rhashtable_walk_start(iter); + + for (;;) { + sc = rhashtable_walk_next(iter); + + if (IS_ERR(sc)) { + if (PTR_ERR(sc) == -EAGAIN) + continue; + err = PTR_ERR(sc); + goto done; + } else if (!sc) { + break; + } + + err = __ioam6_genl_dumpsc_element(sc, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI, + skb, + IOAM6_CMD_DUMP_SCHEMAS); + if (err) + goto done; + } + + err = skb->len; + +done: + rhashtable_walk_stop(iter); + return err; +} + +static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct ioam6_pernet_data *nsdata; + struct ioam6_namespace *ns; + struct ioam6_schema *sc; + __be16 ns_id; + int err = 0; + u32 sc_id; + + if (!info->attrs[IOAM6_ATTR_NS_ID] || + (!info->attrs[IOAM6_ATTR_SC_ID] && + !info->attrs[IOAM6_ATTR_SC_NONE])) + return -EINVAL; + + ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); + nsdata = ioam6_pernet(net); + + mutex_lock(&nsdata->lock); + + ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params); + if (!ns) { + err = -ENOENT; + goto out_unlock; + } + + if (info->attrs[IOAM6_ATTR_SC_NONE]) { + sc = NULL; + } else { + sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); + sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id, + rht_sc_params); + if (!sc) { + err = -ENOENT; + goto out_unlock; + } + } + + if (ns->schema) + ns->schema->ns = NULL; + ns->schema = sc; + + if (sc) { + if (sc->ns) + sc->ns->schema = NULL; + sc->ns = ns; + } + +out_unlock: + mutex_unlock(&nsdata->lock); + return err; +} + +static const struct genl_ops ioam6_genl_ops[] = { + { + .cmd = IOAM6_CMD_ADD_NAMESPACE, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = ioam6_genl_addns, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_DEL_NAMESPACE, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = ioam6_genl_delns, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_DUMP_NAMESPACES, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .start = ioam6_genl_dumpns_start, + .dumpit = ioam6_genl_dumpns, + .done = ioam6_genl_dumpns_done, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_ADD_SCHEMA, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = ioam6_genl_addsc, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_DEL_SCHEMA, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = ioam6_genl_delsc, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_DUMP_SCHEMAS, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .start = ioam6_genl_dumpsc_start, + .dumpit = ioam6_genl_dumpsc, + .done = ioam6_genl_dumpsc_done, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IOAM6_CMD_NS_SET_SCHEMA, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = ioam6_genl_ns_set_schema, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family ioam6_genl_family __ro_after_init = { + .hdrsize = 0, + .name = IOAM6_GENL_NAME, + .version = IOAM6_GENL_VERSION, + .maxattr = IOAM6_ATTR_MAX, + .policy = ioam6_genl_policy, + .netnsok = true, + .parallel_ops = true, + .ops = ioam6_genl_ops, + .n_ops = ARRAY_SIZE(ioam6_genl_ops), + .module = THIS_MODULE, +}; + struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id) { struct ioam6_pernet_data *nsdata = ioam6_pernet(net); @@ -311,16 +814,26 @@ static struct pernet_operations ioam6_net_ops = { int __init ioam6_init(void) { - int err = register_pernet_subsys(&ioam6_net_ops); + int err = genl_register_family(&ioam6_genl_family); + + if (err) + goto out; + err = register_pernet_subsys(&ioam6_net_ops); if (err) - return err; + goto out_unregister_genl; pr_info("In-situ OAM (IOAM) with IPv6\n"); - return 0; + +out: + return err; +out_unregister_genl: + genl_unregister_family(&ioam6_genl_family); + goto out; } void ioam6_exit(void) { unregister_pernet_subsys(&ioam6_net_ops); + genl_unregister_family(&ioam6_genl_family); }