Commit 51cb87c0 authored by David S. Miller's avatar David S. Miller

Merge branch 'ipv6-sr-add-support-for-advanced-local-segment-processing'

David Lebrun says:

====================
ipv6: sr: add support for advanced local segment processing

v2: use EXPORT_SYMBOL_GPL

The current implementation of IPv6 SR supports SRH insertion/encapsulation
and basic segment endpoint behavior (i.e., processing of an SRH contained in
a packet whose active segment (IPv6 DA) is routed to the local node). This
behavior simply consists of updating the DA to the next segment and forwarding
the packet accordingly. This processing is realised for all such packets,
regardless of the active segment.

The most recent specifications of IPv6 SR [1] [2] extend the SRH processing
features as follows. Each segment endpoint defines a MyLocalSID table.
This table maps segments to operations to perform. For each ingress IPv6
packet whose DA is part of a given prefix, the segment endpoint looks
up the active segment (i.e., the IPv6 DA) in the MyLocalSID table and
applies the corresponding operation. Such specifications enable to specify
arbitrary operations besides the basic SRH processing and allow for a more
fine-grained classification.

This patch series implements those extended specifications by leveraging
a new type of lightweight tunnel, seg6local. The MyLocalSID table is
simply an arbitrary routing table (using CONFIG_IPV6_MULTIPLE_TABLES). The
following commands would assign the prefix fc00::/64 to the MyLocalSID
table, map the segment fc00::42 to the regular SRH processing function
(named "End"), and drop all packets received with an undefined active
segment:

ip -6 rule add fc00::/64 lookup 100
ip -6 route add fc00::42 encap seg6local action End dev eth0 table 100
ip -6 route add blackhole default table 100

As another example, the following command would assign the segment
fc00::1234 to the regular SRH processing function, except that the
processed packet must be forwarded to the next-hop fc42::1 (this operation
is named "End.X"):

ip -6 route add fc00::1234 encap seg6local action End.X nh6 fc42::1 dev eth0 table 100

Those two basic operations (End and End.X) are defined in [1]. A more
extensive list of advanced operations is defined in [2].

The first two patches of the series are preliminary work that remove an
assumption about initial SRH format, and export the two functions used to
insert and encapsulate an SRH onto packets. The third patch defines the
new seg6local lightweight tunnel and implement the core functions. The
fourth patch implements the operations needed to handle the newly defined
rtnetlink attributes. The fifth patch implements a few SRH processing
operations, including End and End.X.

[1] https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-07
[2] https://tools.ietf.org/html/draft-filsfils-spring-srv6-network-programming-01
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4cc7b954 140f04c3
#ifndef _LINUX_SEG6_LOCAL_H
#define _LINUX_SEG6_LOCAL_H
#include <uapi/linux/seg6_local.h>
#endif
...@@ -56,7 +56,11 @@ extern int seg6_init(void); ...@@ -56,7 +56,11 @@ extern int seg6_init(void);
extern void seg6_exit(void); extern void seg6_exit(void);
extern int seg6_iptunnel_init(void); extern int seg6_iptunnel_init(void);
extern void seg6_iptunnel_exit(void); extern void seg6_iptunnel_exit(void);
extern int seg6_local_init(void);
extern void seg6_local_exit(void);
extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);
extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
#endif #endif
...@@ -11,6 +11,7 @@ enum lwtunnel_encap_types { ...@@ -11,6 +11,7 @@ enum lwtunnel_encap_types {
LWTUNNEL_ENCAP_IP6, LWTUNNEL_ENCAP_IP6,
LWTUNNEL_ENCAP_SEG6, LWTUNNEL_ENCAP_SEG6,
LWTUNNEL_ENCAP_BPF, LWTUNNEL_ENCAP_BPF,
LWTUNNEL_ENCAP_SEG6_LOCAL,
__LWTUNNEL_ENCAP_MAX, __LWTUNNEL_ENCAP_MAX,
}; };
......
/*
* SR-IPv6 implementation
*
* Author:
* David Lebrun <david.lebrun@uclouvain.be>
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _UAPI_LINUX_SEG6_LOCAL_H
#define _UAPI_LINUX_SEG6_LOCAL_H
#include <linux/seg6.h>
enum {
SEG6_LOCAL_UNSPEC,
SEG6_LOCAL_ACTION,
SEG6_LOCAL_SRH,
SEG6_LOCAL_TABLE,
SEG6_LOCAL_NH4,
SEG6_LOCAL_NH6,
SEG6_LOCAL_IIF,
SEG6_LOCAL_OIF,
__SEG6_LOCAL_MAX,
};
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
enum {
SEG6_LOCAL_ACTION_UNSPEC = 0,
/* node segment */
SEG6_LOCAL_ACTION_END = 1,
/* adjacency segment (IPv6 cross-connect) */
SEG6_LOCAL_ACTION_END_X = 2,
/* lookup of next seg NH in table */
SEG6_LOCAL_ACTION_END_T = 3,
/* decap and L2 cross-connect */
SEG6_LOCAL_ACTION_END_DX2 = 4,
/* decap and IPv6 cross-connect */
SEG6_LOCAL_ACTION_END_DX6 = 5,
/* decap and IPv4 cross-connect */
SEG6_LOCAL_ACTION_END_DX4 = 6,
/* decap and lookup of DA in v6 table */
SEG6_LOCAL_ACTION_END_DT6 = 7,
/* decap and lookup of DA in v4 table */
SEG6_LOCAL_ACTION_END_DT4 = 8,
/* binding segment with insertion */
SEG6_LOCAL_ACTION_END_B6 = 9,
/* binding segment with encapsulation */
SEG6_LOCAL_ACTION_END_B6_ENCAP = 10,
/* binding segment with MPLS encap */
SEG6_LOCAL_ACTION_END_BM = 11,
/* lookup last seg in table */
SEG6_LOCAL_ACTION_END_S = 12,
/* forward to SR-unaware VNF with static proxy */
SEG6_LOCAL_ACTION_END_AS = 13,
/* forward to SR-unaware VNF with masquerading */
SEG6_LOCAL_ACTION_END_AM = 14,
__SEG6_LOCAL_ACTION_MAX,
};
#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1)
#endif
...@@ -44,6 +44,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) ...@@ -44,6 +44,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
return "SEG6"; return "SEG6";
case LWTUNNEL_ENCAP_BPF: case LWTUNNEL_ENCAP_BPF:
return "BPF"; return "BPF";
case LWTUNNEL_ENCAP_SEG6_LOCAL:
return "SEG6LOCAL";
case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP6:
case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_IP:
case LWTUNNEL_ENCAP_NONE: case LWTUNNEL_ENCAP_NONE:
......
...@@ -311,19 +311,8 @@ config IPV6_SEG6_LWTUNNEL ...@@ -311,19 +311,8 @@ config IPV6_SEG6_LWTUNNEL
---help--- ---help---
Support for encapsulation of packets within an outer IPv6 Support for encapsulation of packets within an outer IPv6
header and a Segment Routing Header using the lightweight header and a Segment Routing Header using the lightweight
tunnels mechanism. tunnels mechanism. Also enable support for advanced local
processing of SRv6 packets based on their active segment.
If unsure, say N.
config IPV6_SEG6_INLINE
bool "IPv6: direct Segment Routing Header insertion "
depends on IPV6_SEG6_LWTUNNEL
---help---
Support for direct insertion of the Segment Routing Header,
also known as inline mode. Be aware that direct insertion of
extension headers (as opposed to encapsulation) may break
multiple mechanisms such as PMTUD or IPSec AH. Use this feature
only if you know exactly what you are doing.
If unsure, say N. If unsure, say N.
......
...@@ -23,7 +23,7 @@ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ...@@ -23,7 +23,7 @@ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-$(CONFIG_PROC_FS) += proc.o
ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
ipv6-$(CONFIG_NETLABEL) += calipso.o ipv6-$(CONFIG_NETLABEL) += calipso.o
ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o
ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o
ipv6-objs += $(ipv6-y) ipv6-objs += $(ipv6-y)
......
...@@ -882,7 +882,7 @@ static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto, ...@@ -882,7 +882,7 @@ static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
(hops - 1) * sizeof(struct in6_addr)); (hops - 1) * sizeof(struct in6_addr));
sr_phdr->segments[0] = **addr_p; sr_phdr->segments[0] = **addr_p;
*addr_p = &sr_ihdr->segments[hops - 1]; *addr_p = &sr_ihdr->segments[sr_ihdr->segments_left];
#ifdef CONFIG_IPV6_SEG6_HMAC #ifdef CONFIG_IPV6_SEG6_HMAC
if (sr_has_hmac(sr_phdr)) { if (sr_has_hmac(sr_phdr)) {
...@@ -1174,7 +1174,7 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6, ...@@ -1174,7 +1174,7 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
{ {
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt; struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt;
fl6->daddr = srh->segments[srh->first_segment]; fl6->daddr = srh->segments[srh->segments_left];
break; break;
} }
default: default:
......
...@@ -40,7 +40,7 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len) ...@@ -40,7 +40,7 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
if (((srh->hdrlen + 1) << 3) != len) if (((srh->hdrlen + 1) << 3) != len)
return false; return false;
if (srh->segments_left != srh->first_segment) if (srh->segments_left > srh->first_segment)
return false; return false;
tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4); tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
...@@ -456,6 +456,10 @@ int __init seg6_init(void) ...@@ -456,6 +456,10 @@ int __init seg6_init(void)
err = seg6_iptunnel_init(); err = seg6_iptunnel_init();
if (err) if (err)
goto out_unregister_pernet; goto out_unregister_pernet;
err = seg6_local_init();
if (err)
goto out_unregister_pernet;
#endif #endif
#ifdef CONFIG_IPV6_SEG6_HMAC #ifdef CONFIG_IPV6_SEG6_HMAC
...@@ -471,6 +475,7 @@ int __init seg6_init(void) ...@@ -471,6 +475,7 @@ int __init seg6_init(void)
#ifdef CONFIG_IPV6_SEG6_HMAC #ifdef CONFIG_IPV6_SEG6_HMAC
out_unregister_iptun: out_unregister_iptun:
#ifdef CONFIG_IPV6_SEG6_LWTUNNEL #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
seg6_local_exit();
seg6_iptunnel_exit(); seg6_iptunnel_exit();
#endif #endif
#endif #endif
......
...@@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev, ...@@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev,
} }
/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
{ {
struct net *net = dev_net(skb_dst(skb)->dev); struct net *net = dev_net(skb_dst(skb)->dev);
struct ipv6hdr *hdr, *inner_hdr; struct ipv6hdr *hdr, *inner_hdr;
...@@ -141,10 +141,10 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) ...@@ -141,10 +141,10 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(seg6_do_srh_encap);
/* insert an SRH within an IPv6 packet, just after the IPv6 header */ /* insert an SRH within an IPv6 packet, just after the IPv6 header */
#ifdef CONFIG_IPV6_SEG6_INLINE int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
{ {
struct ipv6hdr *hdr, *oldhdr; struct ipv6hdr *hdr, *oldhdr;
struct ipv6_sr_hdr *isrh; struct ipv6_sr_hdr *isrh;
...@@ -193,7 +193,7 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) ...@@ -193,7 +193,7 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
return 0; return 0;
} }
#endif EXPORT_SYMBOL_GPL(seg6_do_srh_inline);
static int seg6_do_srh(struct sk_buff *skb) static int seg6_do_srh(struct sk_buff *skb)
{ {
...@@ -209,12 +209,10 @@ static int seg6_do_srh(struct sk_buff *skb) ...@@ -209,12 +209,10 @@ static int seg6_do_srh(struct sk_buff *skb)
} }
switch (tinfo->mode) { switch (tinfo->mode) {
#ifdef CONFIG_IPV6_SEG6_INLINE
case SEG6_IPTUN_MODE_INLINE: case SEG6_IPTUN_MODE_INLINE:
err = seg6_do_srh_inline(skb, tinfo->srh); err = seg6_do_srh_inline(skb, tinfo->srh);
skb_reset_inner_headers(skb); skb_reset_inner_headers(skb);
break; break;
#endif
case SEG6_IPTUN_MODE_ENCAP: case SEG6_IPTUN_MODE_ENCAP:
err = seg6_do_srh_encap(skb, tinfo->srh); err = seg6_do_srh_encap(skb, tinfo->srh);
break; break;
...@@ -357,10 +355,8 @@ static int seg6_build_state(struct nlattr *nla, ...@@ -357,10 +355,8 @@ static int seg6_build_state(struct nlattr *nla,
return -EINVAL; return -EINVAL;
switch (tuninfo->mode) { switch (tuninfo->mode) {
#ifdef CONFIG_IPV6_SEG6_INLINE
case SEG6_IPTUN_MODE_INLINE: case SEG6_IPTUN_MODE_INLINE:
break; break;
#endif
case SEG6_IPTUN_MODE_ENCAP: case SEG6_IPTUN_MODE_ENCAP:
break; break;
default: default:
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment