From patchwork Mon Feb 17 22:35:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Aring X-Patchwork-Id: 1239631 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=qeU8AYJl; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48LzPP2fsCz9sRY for ; Tue, 18 Feb 2020 09:36:57 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726182AbgBQWgx (ORCPT ); Mon, 17 Feb 2020 17:36:53 -0500 Received: from mail-qv1-f47.google.com ([209.85.219.47]:36184 "EHLO mail-qv1-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725853AbgBQWgw (ORCPT ); Mon, 17 Feb 2020 17:36:52 -0500 Received: by mail-qv1-f47.google.com with SMTP id db9so8304852qvb.3 for ; Mon, 17 Feb 2020 14:36:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=04qconmLEazi5kRkpGbM2edP1QbpR1jlZYfgCe7OLT0=; b=qeU8AYJlgVoWA7Y8PW4kl3b4LkEyhmNWfCODdHbLFcvijKimP5/KVAKpTmn2KLfzmm v8aniRftIQdTTs/rYSX9atvVhZ7xJBYkVhgFRqEEG8bMtou/BGEEdXp7maFbUHKhmeva xeoI7ru+I9EEkOCs56FFJ+wYppsQmNW5/sm9SJlYzzS3G4hR+YezBVvlaPpd9gd3Jcjo KCIFZaYEfpO2dec8MgGKcbibDKnH0R4pu9M0Lro/X3uDtmpSOoTaQsa3cJkQm6Dfz9YG qTS4FwPbx7JMPQfpcm04sRzerfuqrsRDgscqD6gZfGQ4VoRuyifM9HIkGXSJFDQ4TQ3G fa1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=04qconmLEazi5kRkpGbM2edP1QbpR1jlZYfgCe7OLT0=; b=B0vh5UHB3AFSeCMzdtMNjHxaKfk5nPGjacNDvfrTitDlgdcD9R8tC9J/Ajsank2PcH 4fKWIRcSECUfcSjQqONlGv9RLx3pc9EiVDzvX0jD5/htTxzKFfRpKWcyxtZfSw2Vk5OP 77/FkOi287DUX4gZ68hcs6zaeqWCc/7SddIwWHhAGgJ9Jfv1enKWAoJNUC7eWqrV6a+N TGPh501YTRU7+ThSj+kEyGDlBtwGhYn9/S0Isqtlvh8NXT9lonj+KOQxf35kGaUFDOsN d74Lh6K6cte10pjZn37isDibcUihyC7T9FhaBZVBnqfim067+2+UT5Y1we09Ge/kPXfI bk3g== X-Gm-Message-State: APjAAAXOvmITOyirPKUVJlyTHkXCNQ4QrRNmMJaIFocCRH2EXePUFFEA xwGFIJi48tdS4ko8RoLh37I= X-Google-Smtp-Source: APXvYqyBx4g8c9Sm5/3P8fyGV70F+XzyaJ8NIWZQdRzLtAPH8gQCn3cqgeE7VwGByTm4e+ae2n3Srg== X-Received: by 2002:ad4:59c2:: with SMTP id el2mr13753967qvb.152.1581979010477; Mon, 17 Feb 2020 14:36:50 -0800 (PST) Received: from localhost.localdomain ([216.154.21.195]) by smtp.gmail.com with ESMTPSA id a2sm964031qka.75.2020.02.17.14.36.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Feb 2020 14:36:50 -0800 (PST) From: Alexander Aring To: davem@davemloft.net Cc: kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, kuba@kernel.org, dav.lebrun@gmail.com, mcr@sandelman.ca, stefan@datenfreihafen.org, kai.beckmann@hs-rm.de, martin.gergeleit@hs-rm.de, robert.kaiser@hs-rm.de, netdev@vger.kernel.org, Alexander Aring Subject: [PACTH net-next 1/5] include: uapi: linux: add rpl sr header definition Date: Mon, 17 Feb 2020 17:35:37 -0500 Message-Id: <20200217223541.18862-2-alex.aring@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200217223541.18862-1-alex.aring@gmail.com> References: <20200217223541.18862-1-alex.aring@gmail.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds a uapi header for rpl struct definition. The segments data can be accessed over rpl_segaddr or rpl_segdata macros. In case of compri and compre is zero the segment data is not compressed and can be accessed by rpl_segaddr. In the other case the compressed data can be accessed by rpl_segdata and interpreted as byte array. Signed-off-by: Alexander Aring --- include/uapi/linux/rpl.h | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 include/uapi/linux/rpl.h diff --git a/include/uapi/linux/rpl.h b/include/uapi/linux/rpl.h new file mode 100644 index 000000000000..1dccb55cf8c6 --- /dev/null +++ b/include/uapi/linux/rpl.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * IPv6 RPL-SR implementation + * + * Author: + * (C) 2020 Alexander Aring + */ + +#ifndef _UAPI_LINUX_RPL_H +#define _UAPI_LINUX_RPL_H + +#include +#include +#include + +/* + * RPL SR Header + */ +struct ipv6_rpl_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 cmpre:4, + cmpri:4, + reserved:4, + pad:4, + reserved1:16; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 reserved:20, + pad:4, + cmpri:4, + cmpre:4; +#else +#error "Please fix " +#endif + + union { + struct in6_addr addr[0]; + __u8 data[0]; + } segments; +} __attribute__((packed)); + +#define rpl_segaddr segments.addr +#define rpl_segdata segments.data + +#endif From patchwork Mon Feb 17 22:35:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Aring X-Patchwork-Id: 1239630 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=H8USq53S; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48LzPN6WbFz9sRG for ; Tue, 18 Feb 2020 09:36:56 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726231AbgBQWgx (ORCPT ); Mon, 17 Feb 2020 17:36:53 -0500 Received: from mail-qv1-f66.google.com ([209.85.219.66]:35170 "EHLO mail-qv1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726069AbgBQWgw (ORCPT ); Mon, 17 Feb 2020 17:36:52 -0500 Received: by mail-qv1-f66.google.com with SMTP id u10so8303921qvi.2 for ; Mon, 17 Feb 2020 14:36:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=muLz7NpwuqzQffvLv7y71moNFLRuCrH6WvBmQYEqjwI=; b=H8USq53SNUzxwu21CVgTvrGHPyJ7FdC+7GFXqUG519SbsdIvwipclM3/mfayVd4jgm 4JlhHVucpd0GhGGOIqU5Nw96dszdKr0M8uIj7q22UiQtazI5zCaIfMKVZhXTa82zhbU6 rSvLRPIfsTZ+ykjcis5N9ZeV6tn+qRJvv1unE2L9b01Qe4PEGOI69tJkK+u4YYGazwmS F9aUsqfwAwPwG7yB8iK3bYC4KeSF1c3bYVfSt3lbYr7sKWKYQRbAl1iVDwFeBshijs55 re3/82gUiwCv6cDw9jEbkFH6phmmHO3HlmtayTHWDRsoAdY/U2+LgycceqQ8OMoAQcLa p9Jg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=muLz7NpwuqzQffvLv7y71moNFLRuCrH6WvBmQYEqjwI=; b=T8Pz3SKMkpJzAGqHddCYSCuO3ExSkk+40gfShDbNsHnoT0Ujj7LCLxvHDvcAbPpyuB dJLa5gLkV+sRPmcrqzNgxmYMqdZg7zBydK+cI9Uzd566ATExqpBrTePeZ9GXpWjq2tgg jX0OwuXaS2HhdBMnR3yJtSiITG+fOLSpTaxcn1XCTCXRmGdTfKaSo/9C41X54hQRK4gb dJLsMUYTLje7ewzeqVQXLDaxQQdKzP78G7QdY6HpHuXqKdzxI7vF9yx+VuZ45b2C0jXx 4c7vjvUmHf8Y0CzvzUzz0nt44ujzmjfLzEIChYT9AiMemprZqDzsRuAQwNuHPxxH9KE5 V0OA== X-Gm-Message-State: APjAAAXrNw6MfjEtZ+87AlPdRAaAikcfqsGFHZGfUQyD0V0ff0FPVFw7 RcLKY0Wzez/fWw9U+n70B+M= X-Google-Smtp-Source: APXvYqwalbqTGAteGNYGO3ht8BgkYcUuPktlEFJapSGfMQGk1siGor69q5ADlZXiz4q1Oa7OIGx0rg== X-Received: by 2002:a0c:e2cf:: with SMTP id t15mr14641337qvl.127.1581979011842; Mon, 17 Feb 2020 14:36:51 -0800 (PST) Received: from localhost.localdomain ([216.154.21.195]) by smtp.gmail.com with ESMTPSA id a2sm964031qka.75.2020.02.17.14.36.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Feb 2020 14:36:51 -0800 (PST) From: Alexander Aring To: davem@davemloft.net Cc: kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, kuba@kernel.org, dav.lebrun@gmail.com, mcr@sandelman.ca, stefan@datenfreihafen.org, kai.beckmann@hs-rm.de, martin.gergeleit@hs-rm.de, robert.kaiser@hs-rm.de, netdev@vger.kernel.org, Alexander Aring Subject: [PACTH net-next 2/5] addrconf: add functionality to check on rpl requirements Date: Mon, 17 Feb 2020 17:35:38 -0500 Message-Id: <20200217223541.18862-3-alex.aring@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200217223541.18862-1-alex.aring@gmail.com> References: <20200217223541.18862-1-alex.aring@gmail.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds a functionality to addrconf to check on a specific RPL address configuration. According to RFC 6554: To detect loops in the SRH, a router MUST determine if the SRH includes multiple addresses assigned to any interface on that router. If such addresses appear more than once and are separated by at least one address not assigned to that router. Signed-off-by: Alexander Aring --- include/net/addrconf.h | 3 +++ net/ipv6/addrconf.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index a088349dd94f..e0eabe58aa8b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -90,6 +90,9 @@ int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr); #endif +int ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, + unsigned char nsegs); + bool ipv6_chk_custom_prefix(const struct in6_addr *addr, const unsigned int prefix_len, struct net_device *dev); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index cb493e15959c..66b40ae579a1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4394,6 +4394,59 @@ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) } #endif +/* RFC6554 has some algorithm to avoid loops in segment routing by + * checking if the segments contains any of a local interface address. + * + * Quote: + * + * To detect loops in the SRH, a router MUST determine if the SRH + * includes multiple addresses assigned to any interface on that router. + * If such addresses appear more than once and are separated by at least + * one address not assigned to that router. + */ +int ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, + unsigned char nsegs) +{ + const struct in6_addr *addr; + int i, ret = 0, found = 0; + struct inet6_ifaddr *ifp; + bool separated = false; + unsigned int hash; + bool hash_found; + + rcu_read_lock(); + for (i = 0; i < nsegs; i++) { + addr = &segs[i]; + hash = inet6_addr_hash(net, addr); + + hash_found = false; + hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { + if (!net_eq(dev_net(ifp->idev->dev), net)) + continue; + + if (ipv6_addr_equal(&ifp->addr, addr)) { + hash_found = true; + break; + } + } + + if (hash_found) { + if (found > 1 && separated) { + ret = 1; + break; + } + + separated = false; + found++; + } else { + separated = true; + } + } + rcu_read_unlock(); + + return ret; +} + /* * Periodic address status verification */ From patchwork Mon Feb 17 22:35:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Aring X-Patchwork-Id: 1239632 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=k/Ec9zcK; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48LzPR158Qz9sPK for ; Tue, 18 Feb 2020 09:36:59 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726289AbgBQWg6 (ORCPT ); Mon, 17 Feb 2020 17:36:58 -0500 Received: from mail-qk1-f195.google.com ([209.85.222.195]:33508 "EHLO mail-qk1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726002AbgBQWgz (ORCPT ); Mon, 17 Feb 2020 17:36:55 -0500 Received: by mail-qk1-f195.google.com with SMTP id h4so17792548qkm.0 for ; Mon, 17 Feb 2020 14:36:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Vn338iMu1HtEO9PJ9aN+ZKQY9QX5J9+21xEsfCJ787k=; b=k/Ec9zcKYokB5c5dS2XHPopLMW+Asjg+BRqIUFGNXY98IupztYJppDfrrvHAgNfPgA gnKmPwKNJWJ4Ql/GNrqFMHufMHBxTG8ecCZsZUs3gUkuA1lTiwh1qv0hAW+bDm//b1DT PheRZdSHCm4v8XA9e0lV5U5L8Mo3PHryKeLQNNwNxWdH1YawwIeKAcOqFaJ06LJ5yMwb G2N7B+Xuvrj40yU4Zo4p+/+YMSzzUbhuuWroLem31JCI0bOS8odjz8TvVYlds5ej+x5n GkzlHVLWnBLOE6jbyPJIIPQaxaWWYjXGH2HBWhCniK1aKV57Tsbeit8d5+TAW2A8Rnzj yqAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Vn338iMu1HtEO9PJ9aN+ZKQY9QX5J9+21xEsfCJ787k=; b=nXui+jZ4sfIDuew0aY49JtAgA8xht2SbAozVWKtWrJRB8MGfJMTpSsKWC95yqnIMaQ xAqXKxuIpmANIfrerwJWEMR7/V8WGjHv99tJVywELornlNsMql6dMCZ8bHmCO/tXj0oz OpPBfeHQxNJtXOXr8dvxRHb52MOqOELj3JHOil+D1rJWweI3LW9OU4hRxJ+p+pnD+ceu BrUn65JdqgoUpES5spB6JihQHH+GvdOAwuVbL9GkLaRKTzHJNVaI57ZS6d5DNdq4Mske 2boX4t4t6OCfuTTByOZer7P/yFXSrNtcAr3VaomKUAGdzHOOAlrk6dsn+N8inSjfrwAz 83yQ== X-Gm-Message-State: APjAAAWZ8RIezH9S1OgafuG9WQ/Il5qW4gvBczW957lVJRjTFUfXp1d4 HNiLwf5vHb6S5JtZdIxvJrI= X-Google-Smtp-Source: APXvYqxlF5jdMcQaVSPbSti3SeoSUPvmRX5+saI8D7ef7Ikr4wwa+bCM7EeibnqTr+QLi0/zg28vLw== X-Received: by 2002:a05:620a:15c2:: with SMTP id o2mr2557272qkm.324.1581979013607; Mon, 17 Feb 2020 14:36:53 -0800 (PST) Received: from localhost.localdomain ([216.154.21.195]) by smtp.gmail.com with ESMTPSA id a2sm964031qka.75.2020.02.17.14.36.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Feb 2020 14:36:53 -0800 (PST) From: Alexander Aring To: davem@davemloft.net Cc: kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, kuba@kernel.org, dav.lebrun@gmail.com, mcr@sandelman.ca, stefan@datenfreihafen.org, kai.beckmann@hs-rm.de, martin.gergeleit@hs-rm.de, robert.kaiser@hs-rm.de, netdev@vger.kernel.org, Alexander Aring Subject: [PACTH net-next 3/5] net: ipv6: add support for rpl sr exthdr Date: Mon, 17 Feb 2020 17:35:39 -0500 Message-Id: <20200217223541.18862-4-alex.aring@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200217223541.18862-1-alex.aring@gmail.com> References: <20200217223541.18862-1-alex.aring@gmail.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds rpl source routing receive handling. Everything works only if sysconf "rpl_seg_enabled" and source routing is enabled. Mostly the same behaviour as IPv6 segmentation routing. To handle compression and uncompression a rpl.c file is created which contains the necessary functionality. The receive handling will also care about IPv6 encapsulated so far it's specified as possible nexthdr in RFC 6554. Signed-off-by: Alexander Aring --- include/linux/ipv6.h | 1 + include/net/rpl.h | 34 +++++++ include/uapi/linux/ipv6.h | 2 + net/ipv6/Makefile | 2 +- net/ipv6/addrconf.c | 10 ++ net/ipv6/exthdrs.c | 201 +++++++++++++++++++++++++++++++++++++- net/ipv6/rpl.c | 123 +++++++++++++++++++++++ 7 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 include/net/rpl.h create mode 100644 net/ipv6/rpl.c diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index ea7c7906591e..2cb445a8fc9e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -74,6 +74,7 @@ struct ipv6_devconf { __u32 addr_gen_mode; __s32 disable_policy; __s32 ndisc_tclass; + __s32 rpl_seg_enabled; struct ctl_table_header *sysctl_header; }; diff --git a/include/net/rpl.h b/include/net/rpl.h new file mode 100644 index 000000000000..16739c10cea7 --- /dev/null +++ b/include/net/rpl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * RPL implementation + * + * Author: + * (C) 2020 Alexander Aring + */ + +#ifndef _NET_RPL_H +#define _NET_RPL_H + +#include + +/* Worst decompression memory usage ipv6 address (16) + pad 7 */ +#define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) + +static inline size_t ipv6_rpl_srh_alloc_size(unsigned char n) +{ + return sizeof(struct ipv6_rpl_sr_hdr) + + ((n + 1) * sizeof(struct in6_addr)); +} + +size_t ipv6_rpl_srh_decompress_size(unsigned char n, unsigned char cmpri, + unsigned char cmpre); + +void ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr, + const struct ipv6_rpl_sr_hdr *inhdr, + const struct in6_addr *daddr, unsigned char n); + +void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr, + const struct ipv6_rpl_sr_hdr *inhdr, + const struct in6_addr *daddr, unsigned char n); + +#endif /* _NET_RPL_H */ diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 9c0f4a92bcff..13e8751bf24a 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -40,6 +40,7 @@ struct in6_ifreq { #define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ #define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ +#define IPV6_SRCRT_TYPE_3 3 /* RPL Segment Routing with IPv6 */ #define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ /* @@ -187,6 +188,7 @@ enum { DEVCONF_DISABLE_POLICY, DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_NDISC_TCLASS, + DEVCONF_RPL_SEG_ENABLED, DEVCONF_MAX }; diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 8ccf35514015..9d3e9bd2334f 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -10,7 +10,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ - udp_offload.o seg6.o fib6_notifier.o + udp_offload.o seg6.o fib6_notifier.o rpl.o ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 66b40ae579a1..a0972122d3b4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, .disable_policy = 0, + .rpl_seg_enabled = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -290,6 +291,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, .disable_policy = 0, + .rpl_seg_enabled = 0, }; /* Check if link is ready: is it up and is a valid qdisc available */ @@ -5493,6 +5495,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; + array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; } static inline size_t inet6_ifla6_size(void) @@ -6873,6 +6876,13 @@ static const struct ctl_table addrconf_sysctl[] = { .extra1 = (void *)SYSCTL_ZERO, .extra2 = (void *)&two_five_five, }, + { + .procname = "rpl_seg_enabled", + .data = &ipv6_devconf.rpl_seg_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { /* sentinel */ } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index ab5add0fe6b4..2da9c2722536 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -48,6 +48,7 @@ #ifdef CONFIG_IPV6_SEG6_HMAC #include #endif +#include #include @@ -468,6 +469,195 @@ static int ipv6_srh_rcv(struct sk_buff *skb) return -1; } +static int ipv6_rpl_srh_rcv(struct sk_buff *skb) +{ + struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr; + struct inet6_skb_parm *opt = IP6CB(skb); + struct net *net = dev_net(skb->dev); + struct inet6_dev *idev; + struct ipv6hdr *oldhdr; + struct in6_addr addr; + unsigned char *buf; + int accept_rpl_seg; + int i, err; + u64 n = 0; + u32 r; + + idev = __in6_dev_get(skb->dev); + + accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; + if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) + accept_rpl_seg = idev->cnf.rpl_seg_enabled; + + if (!accept_rpl_seg) { + kfree_skb(skb); + return -1; + } + +looped_back: + hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); + + if (hdr->segments_left == 0) { + if (hdr->nexthdr == NEXTHDR_IPV6) { + int offset = (hdr->hdrlen + 1) << 3; + + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); + + if (!pskb_pull(skb, offset)) { + kfree_skb(skb); + return -1; + } + skb_postpull_rcsum(skb, skb_transport_header(skb), + offset); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->encapsulation = 0; + + __skb_tunnel_rx(skb, skb->dev, net); + + netif_rx(skb); + return -1; + } + + opt->srcrt = skb_network_header_len(skb); + opt->lastopt = opt->srcrt; + skb->transport_header += (hdr->hdrlen + 1) << 3; + opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb); + + return 1; + } + + if (!pskb_may_pull(skb, sizeof(*hdr))) { + kfree_skb(skb); + return -1; + } + + n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre); + r = do_div(n, (16 - hdr->cmpri)); + /* checks if calculation was without remainder and n fits into + * unsigned char which is segments_left field. Should not be + * higher than that. + */ + if (r || (n + 1) > 255) { + kfree_skb(skb); + return -1; + } + + if (hdr->segments_left > n + 1) { + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); + icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, + ((&hdr->segments_left) - + skb_network_header(skb))); + return -1; + } + + if (skb_cloned(skb)) { + if (pskb_expand_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE, 0, + GFP_ATOMIC)) { + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + return -1; + } + } else { + err = skb_cow_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE); + if (unlikely(err)) { + kfree_skb(skb); + return err; + } + } + + hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); + + if (!pskb_may_pull(skb, ipv6_rpl_srh_decompress_size(n, hdr->cmpri, + hdr->cmpre))) { + kfree_skb(skb); + return -1; + } + + hdr->segments_left--; + i = n - hdr->segments_left; + + buf = kzalloc(ipv6_rpl_srh_alloc_size(n + 1) * 2, GFP_ATOMIC); + if (unlikely(!buf)) { + kfree_skb(skb); + return -1; + } + + ohdr = (struct ipv6_rpl_sr_hdr *)buf; + ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n); + chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3)); + + if ((ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST) || + (ipv6_addr_type(&ohdr->rpl_segaddr[i]) & IPV6_ADDR_MULTICAST)) { + kfree_skb(skb); + kfree(buf); + return -1; + } + + err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1); + if (err) { + icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0); + kfree_skb(skb); + kfree(buf); + return -1; + } + + addr = ipv6_hdr(skb)->daddr; + ipv6_hdr(skb)->daddr = ohdr->rpl_segaddr[i]; + ohdr->rpl_segaddr[i] = addr; + + ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n); + + oldhdr = ipv6_hdr(skb); + + skb_pull(skb, ((hdr->hdrlen + 1) << 3)); + skb_postpull_rcsum(skb, oldhdr, + sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); + skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr)); + memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3); + + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_postpush_rcsum(skb, ipv6_hdr(skb), + sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3)); + + kfree(buf); + + skb_dst_drop(skb); + + ip6_route_input(skb); + + if (skb_dst(skb)->error) { + dst_input(skb); + return -1; + } + + if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) { + if (ipv6_hdr(skb)->hop_limit <= 1) { + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0); + kfree_skb(skb); + return -1; + } + ipv6_hdr(skb)->hop_limit--; + + skb_pull(skb, sizeof(struct ipv6hdr)); + goto looped_back; + } + + dst_input(skb); + + return -1; +} + /******************************** Routing header. ********************************/ @@ -506,9 +696,16 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) return -1; } - /* segment routing */ - if (hdr->type == IPV6_SRCRT_TYPE_4) + switch (hdr->type) { + case IPV6_SRCRT_TYPE_4: + /* segment routing */ return ipv6_srh_rcv(skb); + case IPV6_SRCRT_TYPE_3: + /* rpl segment routing */ + return ipv6_rpl_srh_rcv(skb); + default: + break; + } looped_back: if (hdr->segments_left == 0) { diff --git a/net/ipv6/rpl.c b/net/ipv6/rpl.c new file mode 100644 index 000000000000..d1bd1fec2cff --- /dev/null +++ b/net/ipv6/rpl.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * Authors: + * (C) 2020 Alexander Aring + */ + +#include +#include + +#define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x)) + +static void ipv6_rpl_addr_decompress(struct in6_addr *dst, + const struct in6_addr *daddr, + const void *post, unsigned char pfx) +{ + memcpy(dst, daddr, pfx); + memcpy(&dst->s6_addr[pfx], post, IPV6_PFXTAIL_LEN(pfx)); +} + +static void ipv6_rpl_addr_compress(void *dst, const struct in6_addr *addr, + unsigned char pfx) +{ + memcpy(dst, &addr->s6_addr[pfx], IPV6_PFXTAIL_LEN(pfx)); +} + +static void *ipv6_rpl_segdata_pos(const struct ipv6_rpl_sr_hdr *hdr, int i) +{ + return (void *)&hdr->rpl_segdata[i * IPV6_PFXTAIL_LEN(hdr->cmpri)]; +} + +size_t ipv6_rpl_srh_decompress_size(unsigned char n, unsigned char cmpri, + unsigned char cmpre) +{ + return (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); +} + +void ipv6_rpl_srh_decompress(struct ipv6_rpl_sr_hdr *outhdr, + const struct ipv6_rpl_sr_hdr *inhdr, + const struct in6_addr *daddr, unsigned char n) +{ + int i; + + outhdr->nexthdr = inhdr->nexthdr; + outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3); + outhdr->pad = 0; + outhdr->type = inhdr->type; + outhdr->segments_left = inhdr->segments_left; + outhdr->cmpri = 0; + outhdr->cmpre = 0; + + for (i = 0; i <= n; i++) + ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[i], daddr, + ipv6_rpl_segdata_pos(inhdr, i), + inhdr->cmpri); + + ipv6_rpl_addr_decompress(&outhdr->rpl_segaddr[n], daddr, + ipv6_rpl_segdata_pos(inhdr, n), + inhdr->cmpre); +} + +static unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr, + const struct in6_addr *daddr, + unsigned char n) +{ + unsigned char plen; + int i; + + for (plen = 0; plen < sizeof(*daddr); plen++) { + for (i = 0; i <= n; i++) { + if (daddr->s6_addr[plen] != + inhdr->rpl_segaddr[i].s6_addr[plen]) + return plen; + } + } + + return plen; +} + +static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr, + const struct in6_addr *last_segment) +{ + unsigned int plen; + + for (plen = 0; plen < sizeof(*daddr); plen++) { + if (daddr->s6_addr[plen] != last_segment->s6_addr[plen]) + break; + } + + return plen; +} + +void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr, + const struct ipv6_rpl_sr_hdr *inhdr, + const struct in6_addr *daddr, unsigned char n) +{ + unsigned char cmpri, cmpre; + size_t seglen; + int i; + + cmpri = ipv6_rpl_srh_calc_cmpri(inhdr, daddr, n); + cmpre = ipv6_rpl_srh_calc_cmpre(daddr, &inhdr->rpl_segaddr[n]); + + outhdr->nexthdr = inhdr->nexthdr; + seglen = (n * IPV6_PFXTAIL_LEN(cmpri)) + IPV6_PFXTAIL_LEN(cmpre); + outhdr->hdrlen = seglen >> 3; + if (seglen & 0x7) { + outhdr->hdrlen++; + outhdr->pad = 8 - (seglen & 0x7); + } else { + outhdr->pad = 0; + } + outhdr->type = inhdr->type; + outhdr->segments_left = inhdr->segments_left; + outhdr->cmpri = cmpri; + outhdr->cmpre = cmpre; + + for (i = 0; i <= n; i++) + ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, i), + &inhdr->rpl_segaddr[i], cmpri); + + ipv6_rpl_addr_compress(ipv6_rpl_segdata_pos(outhdr, n), + &inhdr->rpl_segaddr[n], cmpre); +} From patchwork Mon Feb 17 22:35:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Aring X-Patchwork-Id: 1239634 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=SQdaaLBh; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48LzPT4191z9sPK for ; Tue, 18 Feb 2020 09:37:01 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726266AbgBQWg5 (ORCPT ); Mon, 17 Feb 2020 17:36:57 -0500 Received: from mail-qk1-f196.google.com ([209.85.222.196]:34626 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726069AbgBQWg4 (ORCPT ); Mon, 17 Feb 2020 17:36:56 -0500 Received: by mail-qk1-f196.google.com with SMTP id c20so17767596qkm.1 for ; Mon, 17 Feb 2020 14:36:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=uo9kSdZ3WMOjPjnxhZzwPm3etI2GMqLIwfvDqeGLBmc=; b=SQdaaLBhUXmr5SgBrjGtHmGNCwaAT7UK5chRn2iZHKNNjM5W15XRgpoPUVJptegNaG m+WJ62FGccDWrmKlvvS6p9P8SZRuh7X5yMPQMhsg/x02E4hKhJXVkC32nxjqJMVZMxdw VJy5CFmMKIeoK3AQV+PL/WkY0fXFOQdMb2/igw/vtSRxxA2slyEAeuNbPC+ucx/Tzgpr ZAVSWR0sfZDNbsJh0kG3v6YO2kt/r0bhVPangyHRz1SOwL+9XUsRGQ2x1YR3OGhvPcK6 GHrhfj/nIEMsMU4n4eilW59h9NdcrPskJpuikCnAqyiKmarYNR9Dw5kv0Po77SmFSe6r IFVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=uo9kSdZ3WMOjPjnxhZzwPm3etI2GMqLIwfvDqeGLBmc=; b=jtcBBt10DOmpK5DNQyFb/SiPsQbOQz5XyEDiqeF/bxqsayc80YMUgd+9UJMtBX2nmd IumjPBW6P3U/BW4GlJKgZPGq/tzsGprWSJuxp34j3Duni6RaZsDMQ+gaCHuKh7vvJEbB xR3l9wXX4qHSdY8Hh0gZ6VFpZBQIIhBptnJoqCYCBArGIzVYpOx5FXYi7yaSqm66cM1b nwAz7iiIg0wQbtrS1ctaL7FzUKQjtRs1eLV/6iU92Qw90/JDMhPRPEiBcnl6Mt6KR2/C M+5puTHgICzhBvUQsp7glOLNqeBZcihzj8JH+OTfdT2KT9HDY9a9stdf4yks4cvDLPm8 jCuw== X-Gm-Message-State: APjAAAUWkE09pEt3NtVPw8XT9zhyGsnCzXTWghznEplQAIFdIsMnrjN1 qJDhHRi8ZMDv2ysfhfT3bRk= X-Google-Smtp-Source: APXvYqz2WlbLgGm3ycHSs6BY7tymqKQ0ilW/MXrKoTPjADQccdxtavkYTc/gJvNoK/8vk8b235soWA== X-Received: by 2002:a37:8343:: with SMTP id f64mr15682364qkd.21.1581979015705; Mon, 17 Feb 2020 14:36:55 -0800 (PST) Received: from localhost.localdomain ([216.154.21.195]) by smtp.gmail.com with ESMTPSA id a2sm964031qka.75.2020.02.17.14.36.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Feb 2020 14:36:55 -0800 (PST) From: Alexander Aring To: davem@davemloft.net Cc: kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, kuba@kernel.org, dav.lebrun@gmail.com, mcr@sandelman.ca, stefan@datenfreihafen.org, kai.beckmann@hs-rm.de, martin.gergeleit@hs-rm.de, robert.kaiser@hs-rm.de, netdev@vger.kernel.org, Alexander Aring Subject: [PACTH net-next 4/5] net: add net available in build_state Date: Mon, 17 Feb 2020 17:35:40 -0500 Message-Id: <20200217223541.18862-5-alex.aring@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200217223541.18862-1-alex.aring@gmail.com> References: <20200217223541.18862-1-alex.aring@gmail.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The build_state callback of lwtunnel doesn't contain the net namespace structure yet. This patch will add it so we can check on specific address configuration at creation time of rpl source routes. Signed-off-by: Alexander Aring --- include/net/ip_fib.h | 5 +++-- include/net/lwtunnel.h | 4 ++-- net/core/lwt_bpf.c | 2 +- net/core/lwtunnel.c | 4 ++-- net/ipv4/fib_lookup.h | 2 +- net/ipv4/fib_semantics.c | 22 ++++++++++++---------- net/ipv4/fib_trie.c | 2 +- net/ipv4/ip_tunnel_core.c | 4 ++-- net/ipv6/ila/ila_lwt.c | 2 +- net/ipv6/route.c | 2 +- net/ipv6/seg6_iptunnel.c | 2 +- net/ipv6/seg6_local.c | 5 +++-- net/mpls/mpls_iptunnel.c | 2 +- 13 files changed, 31 insertions(+), 27 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 6a1ae49809de..74f86ebbfcdf 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -470,8 +470,9 @@ int fib_nh_init(struct net *net, struct fib_nh *fib_nh, struct fib_config *cfg, int nh_weight, struct netlink_ext_ack *extack); void fib_nh_release(struct net *net, struct fib_nh *fib_nh); -int fib_nh_common_init(struct fib_nh_common *nhc, struct nlattr *fc_encap, - u16 fc_encap_type, void *cfg, gfp_t gfp_flags, +int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc, + struct nlattr *fc_encap, u16 fc_encap_type, + void *cfg, gfp_t gfp_flags, struct netlink_ext_ack *extack); void fib_nh_common_release(struct fib_nh_common *nhc); diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index 5d6c5b1fc695..a7f0a857cadf 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -34,7 +34,7 @@ struct lwtunnel_state { }; struct lwtunnel_encap_ops { - int (*build_state)(struct nlattr *encap, + int (*build_state)(struct net *net, struct nlattr *encap, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack); @@ -113,7 +113,7 @@ int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack); int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len, struct netlink_ext_ack *extack); -int lwtunnel_build_state(u16 encap_type, +int lwtunnel_build_state(struct net *net, u16 encap_type, struct nlattr *encap, unsigned int family, const void *cfg, struct lwtunnel_state **lws, diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 99a6de52b21d..7d3438215f32 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -367,7 +367,7 @@ static const struct nla_policy bpf_nl_policy[LWT_BPF_MAX + 1] = { [LWT_BPF_XMIT_HEADROOM] = { .type = NLA_U32 }, }; -static int bpf_build_state(struct nlattr *nla, +static int bpf_build_state(struct net *net, struct nlattr *nla, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 2f9c0de533c7..4cd03955fa32 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -98,7 +98,7 @@ int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, } EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops); -int lwtunnel_build_state(u16 encap_type, +int lwtunnel_build_state(struct net *net, u16 encap_type, struct nlattr *encap, unsigned int family, const void *cfg, struct lwtunnel_state **lws, struct netlink_ext_ack *extack) @@ -122,7 +122,7 @@ int lwtunnel_build_state(u16 encap_type, rcu_read_unlock(); if (found) { - ret = ops->build_state(encap, family, cfg, lws, extack); + ret = ops->build_state(net, encap, family, cfg, lws, extack); if (ret) module_put(ops->owner); } else { diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index c092e9a55790..818916b2a04d 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -35,7 +35,7 @@ static inline void fib_alias_accessed(struct fib_alias *fa) void fib_release_info(struct fib_info *); struct fib_info *fib_create_info(struct fib_config *cfg, struct netlink_ext_ack *extack); -int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, +int fib_nh_match(struct net *net, struct fib_config *cfg, struct fib_info *fi, struct netlink_ext_ack *extack); bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi); int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index a803cdd9400a..04494f8a5c65 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -570,8 +570,9 @@ static int fib_detect_death(struct fib_info *fi, int order, return 1; } -int fib_nh_common_init(struct fib_nh_common *nhc, struct nlattr *encap, - u16 encap_type, void *cfg, gfp_t gfp_flags, +int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc, + struct nlattr *encap, u16 encap_type, + void *cfg, gfp_t gfp_flags, struct netlink_ext_ack *extack) { int err; @@ -589,8 +590,9 @@ int fib_nh_common_init(struct fib_nh_common *nhc, struct nlattr *encap, err = -EINVAL; goto lwt_failure; } - err = lwtunnel_build_state(encap_type, encap, nhc->nhc_family, - cfg, &lwtstate, extack); + err = lwtunnel_build_state(net, encap_type, encap, + nhc->nhc_family, cfg, &lwtstate, + extack); if (err) goto lwt_failure; @@ -614,7 +616,7 @@ int fib_nh_init(struct net *net, struct fib_nh *nh, nh->fib_nh_family = AF_INET; - err = fib_nh_common_init(&nh->nh_common, cfg->fc_encap, + err = fib_nh_common_init(net, &nh->nh_common, cfg->fc_encap, cfg->fc_encap_type, cfg, GFP_KERNEL, extack); if (err) return err; @@ -814,7 +816,7 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, #endif /* CONFIG_IP_ROUTE_MULTIPATH */ -static int fib_encap_match(u16 encap_type, +static int fib_encap_match(struct net *net, u16 encap_type, struct nlattr *encap, const struct fib_nh *nh, const struct fib_config *cfg, @@ -826,7 +828,7 @@ static int fib_encap_match(u16 encap_type, if (encap_type == LWTUNNEL_ENCAP_NONE) return 0; - ret = lwtunnel_build_state(encap_type, encap, AF_INET, + ret = lwtunnel_build_state(net, encap_type, encap, AF_INET, cfg, &lwtstate, extack); if (!ret) { result = lwtunnel_cmp_encap(lwtstate, nh->fib_nh_lws); @@ -836,7 +838,7 @@ static int fib_encap_match(u16 encap_type, return result; } -int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, +int fib_nh_match(struct net *net, struct fib_config *cfg, struct fib_info *fi, struct netlink_ext_ack *extack) { #ifdef CONFIG_IP_ROUTE_MULTIPATH @@ -857,8 +859,8 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, struct fib_nh *nh = fib_info_nh(fi, 0); if (cfg->fc_encap) { - if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap, - nh, cfg, extack)) + if (fib_encap_match(net, cfg->fc_encap_type, + cfg->fc_encap, nh, cfg, extack)) return 1; } #ifdef CONFIG_IP_ROUTE_CLASSID diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index ff0c24371e33..196b265030f7 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1684,7 +1684,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, fi->fib_prefsrc == cfg->fc_prefsrc) && (!cfg->fc_protocol || fi->fib_protocol == cfg->fc_protocol) && - fib_nh_match(cfg, fi, extack) == 0 && + fib_nh_match(net, cfg, fi, extack) == 0 && fib_metrics_match(cfg, fi)) { fa_to_delete = fa; break; diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 47f8b947eef1..181b7a2a0247 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -432,7 +432,7 @@ static int ip_tun_set_opts(struct nlattr *attr, struct ip_tunnel_info *info, return ip_tun_parse_opts(attr, info, extack); } -static int ip_tun_build_state(struct nlattr *attr, +static int ip_tun_build_state(struct net *net, struct nlattr *attr, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) @@ -719,7 +719,7 @@ static const struct nla_policy ip6_tun_policy[LWTUNNEL_IP6_MAX + 1] = { [LWTUNNEL_IP6_OPTS] = { .type = NLA_NESTED }, }; -static int ip6_tun_build_state(struct nlattr *attr, +static int ip6_tun_build_state(struct net *net, struct nlattr *attr, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 422dcc691f71..8c1ce78956ba 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -125,7 +125,7 @@ static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { [ILA_ATTR_HOOK_TYPE] = { .type = NLA_U8, }, }; -static int ila_build_state(struct nlattr *nla, +static int ila_build_state(struct net *net, struct nlattr *nla, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4fbdc60b4e07..7283c9abeaa8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3475,7 +3475,7 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh, !netif_carrier_ok(dev)) fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN; - err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap, + err = fib_nh_common_init(net, &fib6_nh->nh_common, cfg->fc_encap, cfg->fc_encap_type, cfg, gfp_flags, extack); if (err) goto out; diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index ab7f124ff5d7..7f64bb668d40 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -376,7 +376,7 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) return err; } -static int seg6_build_state(struct nlattr *nla, +static int seg6_build_state(struct net *net, struct nlattr *nla, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 7cbc19731997..e3f1a292aa01 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -970,8 +970,9 @@ static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) return 0; } -static int seg6_local_build_state(struct nlattr *nla, unsigned int family, - const void *cfg, struct lwtunnel_state **ts, +static int seg6_local_build_state(struct net *net, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts, struct netlink_ext_ack *extack) { struct nlattr *tb[SEG6_LOCAL_MAX + 1]; diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index 44b675016393..2def85718d94 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -162,7 +162,7 @@ static int mpls_xmit(struct sk_buff *skb) return -EINVAL; } -static int mpls_build_state(struct nlattr *nla, +static int mpls_build_state(struct net *net, struct nlattr *nla, unsigned int family, const void *cfg, struct lwtunnel_state **ts, struct netlink_ext_ack *extack) From patchwork Mon Feb 17 22:35:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Aring X-Patchwork-Id: 1239633 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=iXat5Lvs; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 48LzPS1bylz9sRG for ; Tue, 18 Feb 2020 09:37:00 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726298AbgBQWg7 (ORCPT ); Mon, 17 Feb 2020 17:36:59 -0500 Received: from mail-qk1-f196.google.com ([209.85.222.196]:45852 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726276AbgBQWg6 (ORCPT ); Mon, 17 Feb 2020 17:36:58 -0500 Received: by mail-qk1-f196.google.com with SMTP id a2so17694743qko.12 for ; Mon, 17 Feb 2020 14:36:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QHzc8tFUF/YXzcbc56FmSm9D8jpXUubUmCOzcMGmO/c=; b=iXat5LvscTYehemRNJizvLwtVuY1JlEQL78MG97xli7pwlbw+1WCiAoNA2eoEIyBnf MrT8Lvc1zp+ygMe1RonB6Ttp5bXGUWTzyIH1lUkc7XXg16xOwU/7MxHe4xdsXhnURsrP AzPPOK6GOCn85l0JW8wuZekljs6ucQMJ/luQyvmHw3J1xAA+FMFVthMJJ4z7+0fCMM4s E4vJqlm0A2W50yfOvHFbktGlDrcZ2qZH0gvuNRTH6gnZm8khi67MWV0IUzsXH5fwx5oi M/rf1MNgSzC/aKYsk0O0zJNfX+jckKBj8hQL6QzveBlFbhxIBP6xzlKkBsRqUTEvMmHe B9Zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QHzc8tFUF/YXzcbc56FmSm9D8jpXUubUmCOzcMGmO/c=; b=exJ/9mFYQ6PCIzqkoD0dYcA2/I0DfArN76Im/DyPhx3dfCI3yc7yxdfdFDDkOzAf+9 SSGx4zuG8Sr8pF8ZEONWpcz6DRHsRRQJUqDSmq39um10GyO3ofZ9KHKcn6/BXzypjD5o uT+QzlJVcYwAiu0QkhUSFT2iak0tsx/edChlLEEx8vZPpSQFoOAgRUZvU8alpgKYSJdN o+3F8BZL+4UfrttukyATi3jgowMfVvVR+vjUZu+aGjnU1eFykzy2BOqaQzNW+xa/jzuc NQFMh77K1+uQWzVZQcoZ6NDrzVs/3E74EvE1ywttpnVrEH0z47fKKmf1Rtblof2caOpN AmLw== X-Gm-Message-State: APjAAAWZG7AdRAGLGdHzwuxvzcrcl/+ijbudNuCCrnkEPPIH3R5DseLF ARsFAG5oQNOAeUsyLc6c+p8= X-Google-Smtp-Source: APXvYqxuA6SYzUR3lE4sxrnUwiys5Et8CMd8FnkINOmEQ4H1PanT56MIXvjLgZ/2UooPBUFyUNLsYg== X-Received: by 2002:a37:a7d0:: with SMTP id q199mr927655qke.488.1581979017503; Mon, 17 Feb 2020 14:36:57 -0800 (PST) Received: from localhost.localdomain ([216.154.21.195]) by smtp.gmail.com with ESMTPSA id a2sm964031qka.75.2020.02.17.14.36.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Feb 2020 14:36:57 -0800 (PST) From: Alexander Aring To: davem@davemloft.net Cc: kuznet@ms2.inr.ac.ru, yoshfuji@linux-ipv6.org, kuba@kernel.org, dav.lebrun@gmail.com, mcr@sandelman.ca, stefan@datenfreihafen.org, kai.beckmann@hs-rm.de, martin.gergeleit@hs-rm.de, robert.kaiser@hs-rm.de, netdev@vger.kernel.org, Alexander Aring Subject: [PACTH net-next 5/5] net: ipv6: add rpl sr tunnel Date: Mon, 17 Feb 2020 17:35:41 -0500 Message-Id: <20200217223541.18862-6-alex.aring@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200217223541.18862-1-alex.aring@gmail.com> References: <20200217223541.18862-1-alex.aring@gmail.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds functionality to configure routes for RPL source routing functionality. There is no IPIP functionality yet implemented which can be added later when the cases when to use IPv6 encapuslation comes more clear. Signed-off-by: Alexander Aring --- include/net/rpl.h | 12 + include/uapi/linux/lwtunnel.h | 1 + include/uapi/linux/rpl_iptunnel.h | 21 ++ net/core/lwtunnel.c | 2 + net/ipv6/Kconfig | 10 + net/ipv6/Makefile | 1 + net/ipv6/af_inet6.c | 7 + net/ipv6/rpl_iptunnel.c | 375 ++++++++++++++++++++++++++++++ 8 files changed, 429 insertions(+) create mode 100644 include/uapi/linux/rpl_iptunnel.h create mode 100644 net/ipv6/rpl_iptunnel.c diff --git a/include/net/rpl.h b/include/net/rpl.h index 16739c10cea7..67b4266770e0 100644 --- a/include/net/rpl.h +++ b/include/net/rpl.h @@ -11,6 +11,18 @@ #include +#if IS_ENABLED(CONFIG_IPV6_RPL_LWTUNNEL) +extern int rpl_init(void); +extern void rpl_exit(void); +#else +static inline int rpl_init(void) +{ + return 0; +} + +static inline void rpl_exit(void) {} +#endif + /* Worst decompression memory usage ipv6 address (16) + pad 7 */ #define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h index f6035f737193..568a4303ccce 100644 --- a/include/uapi/linux/lwtunnel.h +++ b/include/uapi/linux/lwtunnel.h @@ -13,6 +13,7 @@ enum lwtunnel_encap_types { LWTUNNEL_ENCAP_SEG6, LWTUNNEL_ENCAP_BPF, LWTUNNEL_ENCAP_SEG6_LOCAL, + LWTUNNEL_ENCAP_RPL, __LWTUNNEL_ENCAP_MAX, }; diff --git a/include/uapi/linux/rpl_iptunnel.h b/include/uapi/linux/rpl_iptunnel.h new file mode 100644 index 000000000000..f4eed1f92baa --- /dev/null +++ b/include/uapi/linux/rpl_iptunnel.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * IPv6 RPL-SR implementation + * + * Author: + * (C) 2020 Alexander Aring + */ + +#ifndef _UAPI_LINUX_RPL_IPTUNNEL_H +#define _UAPI_LINUX_RPL_IPTUNNEL_H + +enum { + RPL_IPTUNNEL_UNSPEC, + RPL_IPTUNNEL_SRH, + __RPL_IPTUNNEL_MAX, +}; +#define RPL_IPTUNNEL_MAX (__RPL_IPTUNNEL_MAX - 1) + +#define RPL_IPTUNNEL_SRH_SIZE(srh) (((srh)->hdrlen + 1) << 3) + +#endif diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 4cd03955fa32..8ec7d13d2860 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -41,6 +41,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) return "BPF"; case LWTUNNEL_ENCAP_SEG6_LOCAL: return "SEG6LOCAL"; + case LWTUNNEL_ENCAP_RPL: + return "RPL"; case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_NONE: diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index ae1344e4cec5..2ccaee98fddb 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -303,4 +303,14 @@ config IPV6_SEG6_BPF depends on IPV6_SEG6_LWTUNNEL depends on IPV6 = y +config IPV6_RPL_LWTUNNEL + bool "IPv6: RPL Source Routing Header support" + depends on IPV6 + select LWTUNNEL + ---help--- + Support for RFC6554 RPL Source Routing Header using the lightweight + tunnels mechanism. + + If unsure, say N. + endif # IPV6 diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 9d3e9bd2334f..cf7b47bdb9b3 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -26,6 +26,7 @@ ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o ipv6-$(CONFIG_NETLABEL) += calipso.o ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o +ipv6-$(CONFIG_IPV6_RPL_LWTUNNEL) += rpl_iptunnel.o ipv6-objs += $(ipv6-y) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index d727c3b41495..345baa0a754f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -59,6 +59,7 @@ #endif #include #include +#include #include #include @@ -1114,6 +1115,10 @@ static int __init inet6_init(void) if (err) goto seg6_fail; + err = rpl_init(); + if (err) + goto rpl_fail; + err = igmp6_late_init(); if (err) goto igmp6_late_err; @@ -1136,6 +1141,8 @@ static int __init inet6_init(void) igmp6_late_cleanup(); #endif igmp6_late_err: + rpl_exit(); +rpl_fail: seg6_exit(); seg6_fail: calipso_exit(); diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c new file mode 100644 index 000000000000..dc3d67d93210 --- /dev/null +++ b/net/ipv6/rpl_iptunnel.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * Authors: + * (C) 2020 Alexander Aring + */ + +#include + +#include +#include +#include +#include +#include + +struct rpl_iptunnel_encap { + struct ipv6_rpl_sr_hdr srh[0]; +}; + +struct rpl_lwt { + struct dst_cache cache; + struct rpl_iptunnel_encap tuninfo; +}; + +static inline struct rpl_lwt *rpl_lwt_lwtunnel(struct lwtunnel_state *lwt) +{ + return (struct rpl_lwt *)lwt->data; +} + +static inline struct rpl_iptunnel_encap * +rpl_encap_lwtunnel(struct lwtunnel_state *lwt) +{ + return &rpl_lwt_lwtunnel(lwt)->tuninfo; +} + +static const struct nla_policy rpl_iptunnel_policy[RPL_IPTUNNEL_MAX + 1] = { + [RPL_IPTUNNEL_SRH] = { .type = NLA_BINARY }, +}; + +static bool rpl_validate_srh(struct net *net, struct ipv6_rpl_sr_hdr *srh, + size_t seglen) +{ + int err; + + if ((srh->hdrlen << 3) != seglen) + return false; + + if (srh->cmpri || srh->cmpre) + return false; + + err = ipv6_chk_rpl_srh_loop(net, srh->rpl_segaddr, + srh->segments_left); + if (err) + return false; + + if (ipv6_addr_type(&srh->rpl_segaddr[srh->segments_left - 1]) & + IPV6_ADDR_MULTICAST) + return false; + + return true; +} + +static int rpl_build_state(struct net *net, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[RPL_IPTUNNEL_MAX + 1]; + struct lwtunnel_state *newts; + struct ipv6_rpl_sr_hdr *srh; + struct rpl_lwt *rlwt; + int err, srh_len; + + if (family != AF_INET6) + return -EINVAL; + + err = nla_parse_nested_deprecated(tb, RPL_IPTUNNEL_MAX, nla, + rpl_iptunnel_policy, extack); + if (err < 0) + return err; + + if (!tb[RPL_IPTUNNEL_SRH]) + return -EINVAL; + + srh = nla_data(tb[RPL_IPTUNNEL_SRH]); + srh_len = nla_len(tb[RPL_IPTUNNEL_SRH]); + + if (srh_len < sizeof(*srh)) + return -EINVAL; + + /* verify that SRH is consistent */ + if (!rpl_validate_srh(net, srh, srh_len - sizeof(*srh))) + return -EINVAL; + + newts = lwtunnel_state_alloc(srh_len + sizeof(*rlwt)); + if (!newts) + return -ENOMEM; + + rlwt = rpl_lwt_lwtunnel(newts); + + err = dst_cache_init(&rlwt->cache, GFP_ATOMIC); + if (err) { + kfree(newts); + return err; + } + + memcpy(&rlwt->tuninfo.srh, srh, srh_len); + + newts->type = LWTUNNEL_ENCAP_RPL; + newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; + + *ts = newts; + + return 0; +} + +static void rpl_destroy_state(struct lwtunnel_state *lwt) +{ + dst_cache_destroy(&rpl_lwt_lwtunnel(lwt)->cache); +} + +static int rpl_do_srh_inline(struct sk_buff *skb, const struct rpl_lwt *rlwt, + const struct ipv6_rpl_sr_hdr *srh) +{ + struct ipv6_rpl_sr_hdr *isrh, *csrh; + const struct ipv6hdr *oldhdr; + struct ipv6hdr *hdr; + unsigned char *buf; + size_t hdrlen; + int err; + + oldhdr = ipv6_hdr(skb); + + buf = kzalloc(ipv6_rpl_srh_alloc_size(srh->segments_left - 1) * 2, + GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + isrh = (struct ipv6_rpl_sr_hdr *)buf; + csrh = (struct ipv6_rpl_sr_hdr *)(buf + ((srh->hdrlen + 1) << 3)); + + memcpy(isrh, srh, sizeof(*isrh)); + memcpy(isrh->rpl_segaddr, &srh->rpl_segaddr[1], + (srh->segments_left - 1) * 16); + isrh->rpl_segaddr[srh->segments_left - 1] = oldhdr->daddr; + + ipv6_rpl_srh_compress(csrh, isrh, &srh->rpl_segaddr[0], + isrh->segments_left - 1); + + hdrlen = ((csrh->hdrlen + 1) << 3); + + err = skb_cow_head(skb, hdrlen + skb->mac_len); + if (unlikely(err)) + return err; + + skb_pull(skb, sizeof(struct ipv6hdr)); + skb_postpull_rcsum(skb, skb_network_header(skb), + sizeof(struct ipv6hdr)); + + skb_push(skb, sizeof(struct ipv6hdr) + hdrlen); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); + + hdr = ipv6_hdr(skb); + memmove(hdr, oldhdr, sizeof(*hdr)); + isrh = (void *)hdr + sizeof(*hdr); + memcpy(isrh, csrh, hdrlen); + + isrh->nexthdr = hdr->nexthdr; + hdr->nexthdr = NEXTHDR_ROUTING; + hdr->daddr = srh->rpl_segaddr[0]; + + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen); + + kfree(buf); + + return 0; +} + +static int rpl_do_srh(struct sk_buff *skb, const struct rpl_lwt *rlwt) +{ + struct dst_entry *dst = skb_dst(skb); + struct rpl_iptunnel_encap *tinfo; + int err = 0; + + if (skb->protocol != htons(ETH_P_IPV6)) + return -EINVAL; + + tinfo = rpl_encap_lwtunnel(dst->lwtstate); + + err = rpl_do_srh_inline(skb, rlwt, tinfo->srh); + if (err) + return err; + + return 0; +} + +static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct dst_entry *orig_dst = skb_dst(skb); + struct dst_entry *dst = NULL; + struct rpl_lwt *rlwt; + int err = -EINVAL; + + rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate); + + err = rpl_do_srh(skb, rlwt); + if (unlikely(err)) + goto drop; + + preempt_disable(); + dst = dst_cache_get(&rlwt->cache); + preempt_enable(); + + if (unlikely(!dst)) { + struct ipv6hdr *hdr = ipv6_hdr(skb); + struct flowi6 fl6; + + memset(&fl6, 0, sizeof(fl6)); + fl6.daddr = hdr->daddr; + fl6.saddr = hdr->saddr; + fl6.flowlabel = ip6_flowinfo(hdr); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = hdr->nexthdr; + + dst = ip6_route_output(net, NULL, &fl6); + if (dst->error) { + err = dst->error; + dst_release(dst); + goto drop; + } + + preempt_disable(); + dst_cache_set_ip6(&rlwt->cache, dst, &fl6.saddr); + preempt_enable(); + } + + skb_dst_drop(skb); + skb_dst_set(skb, dst); + + err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); + if (unlikely(err)) + goto drop; + + return dst_output(net, sk, skb); + +drop: + kfree_skb(skb); + return err; +} + +static int rpl_input(struct sk_buff *skb) +{ + struct dst_entry *orig_dst = skb_dst(skb); + struct dst_entry *dst = NULL; + struct rpl_lwt *rlwt; + int err; + + rlwt = rpl_lwt_lwtunnel(orig_dst->lwtstate); + + err = rpl_do_srh(skb, rlwt); + if (unlikely(err)) { + kfree_skb(skb); + return err; + } + + preempt_disable(); + dst = dst_cache_get(&rlwt->cache); + preempt_enable(); + + skb_dst_drop(skb); + + if (!dst) { + ip6_route_input(skb); + dst = skb_dst(skb); + if (!dst->error) { + preempt_disable(); + dst_cache_set_ip6(&rlwt->cache, dst, + &ipv6_hdr(skb)->saddr); + preempt_enable(); + } + } else { + skb_dst_set(skb, dst); + } + + err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); + if (unlikely(err)) + return err; + + return dst_input(skb); +} + +static int nla_put_rpl_srh(struct sk_buff *skb, int attrtype, + struct rpl_iptunnel_encap *tuninfo) +{ + struct rpl_iptunnel_encap *data; + struct nlattr *nla; + int len; + + len = RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh); + + nla = nla_reserve(skb, attrtype, len); + if (!nla) + return -EMSGSIZE; + + data = nla_data(nla); + memcpy(data, tuninfo->srh, len); + + return 0; +} + +static int rpl_fill_encap_info(struct sk_buff *skb, + struct lwtunnel_state *lwtstate) +{ + struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate); + + if (nla_put_rpl_srh(skb, RPL_IPTUNNEL_SRH, tuninfo)) + return -EMSGSIZE; + + return 0; +} + +static int rpl_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + struct rpl_iptunnel_encap *tuninfo = rpl_encap_lwtunnel(lwtstate); + + return nla_total_size(RPL_IPTUNNEL_SRH_SIZE(tuninfo->srh)); +} + +static int rpl_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct rpl_iptunnel_encap *a_hdr = rpl_encap_lwtunnel(a); + struct rpl_iptunnel_encap *b_hdr = rpl_encap_lwtunnel(b); + int len = RPL_IPTUNNEL_SRH_SIZE(a_hdr->srh); + + if (len != RPL_IPTUNNEL_SRH_SIZE(b_hdr->srh)) + return 1; + + return memcmp(a_hdr, b_hdr, len); +} + +static const struct lwtunnel_encap_ops rpl_ops = { + .build_state = rpl_build_state, + .destroy_state = rpl_destroy_state, + .output = rpl_output, + .input = rpl_input, + .fill_encap = rpl_fill_encap_info, + .get_encap_size = rpl_encap_nlsize, + .cmp_encap = rpl_encap_cmp, + .owner = THIS_MODULE, +}; + +int __init rpl_init(void) +{ + int err; + + err = lwtunnel_encap_add_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL); + if (err) + goto out; + + pr_info("RPL Segment Routing with IPv6\n"); + + return 0; + +out: + return err; +} + +void rpl_exit(void) +{ + lwtunnel_encap_del_ops(&rpl_ops, LWTUNNEL_ENCAP_RPL); +}