Skip to content

Commit

Permalink
ipv6: sr: add support for SRH encapsulation and injection with lwtunnels
Browse files Browse the repository at this point in the history
This patch creates a new type of interfaceless lightweight tunnel (SEG6),
enabling the encapsulation and injection of SRH within locally emitted
packets and forwarded packets.

>From a configuration viewpoint, a seg6 tunnel would be configured as follows:

  ip -6 ro ad fc00::1/128 encap seg6 mode encap segs fc42::1,fc42::2,fc42::3 dev eth0

Any packet whose destination address is fc00::1 would thus be encapsulated
within an outer IPv6 header containing the SRH with three segments, and would
actually be routed to the first segment of the list. If `mode inline' was
specified instead of `mode encap', then the SRH would be directly inserted
after the IPv6 header without outer encapsulation.

The inline mode is only available if CONFIG_IPV6_SEG6_INLINE is enabled. This
feature was made configurable because direct header insertion may break
several mechanisms such as PMTUD or IPSec AH.

Signed-off-by: David Lebrun <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
David Lebrun authored and davem330 committed Nov 10, 2016
1 parent 915d7e5 commit 6c8702c
Show file tree
Hide file tree
Showing 9 changed files with 526 additions and 1 deletion.
6 changes: 6 additions & 0 deletions include/linux/seg6_iptunnel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef _LINUX_SEG6_IPTUNNEL_H
#define _LINUX_SEG6_IPTUNNEL_H

#include <uapi/linux/seg6_iptunnel.h>

#endif
6 changes: 6 additions & 0 deletions include/net/seg6.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include <linux/net.h>
#include <linux/ipv6.h>
#include <net/lwtunnel.h>
#include <linux/seg6.h>

static inline void update_csum_diff4(struct sk_buff *skb, __be32 from,
__be32 to)
Expand Down Expand Up @@ -48,5 +50,9 @@ static inline struct seg6_pernet_data *seg6_pernet(struct net *net)

extern int seg6_init(void);
extern void seg6_exit(void);
extern int seg6_iptunnel_init(void);
extern void seg6_iptunnel_exit(void);

extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);

#endif
1 change: 1 addition & 0 deletions include/uapi/linux/lwtunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum lwtunnel_encap_types {
LWTUNNEL_ENCAP_IP,
LWTUNNEL_ENCAP_ILA,
LWTUNNEL_ENCAP_IP6,
LWTUNNEL_ENCAP_SEG6,
__LWTUNNEL_ENCAP_MAX,
};

Expand Down
44 changes: 44 additions & 0 deletions include/uapi/linux/seg6_iptunnel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SR-IPv6 implementation
*
* Author:
* David Lebrun <[email protected]>
*
*
* 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_IPTUNNEL_H
#define _UAPI_LINUX_SEG6_IPTUNNEL_H

enum {
SEG6_IPTUNNEL_UNSPEC,
SEG6_IPTUNNEL_SRH,
__SEG6_IPTUNNEL_MAX,
};
#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)

struct seg6_iptunnel_encap {
int mode;
struct ipv6_sr_hdr srh[0];
};

#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3))

enum {
SEG6_IPTUN_MODE_INLINE,
SEG6_IPTUN_MODE_ENCAP,
};

static inline size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
{
int encap = (tuninfo->mode == SEG6_IPTUN_MODE_ENCAP);

return ((tuninfo->srh->hdrlen + 1) << 3) +
(encap * sizeof(struct ipv6hdr));
}

#endif
2 changes: 2 additions & 0 deletions net/core/lwtunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
return "MPLS";
case LWTUNNEL_ENCAP_ILA:
return "ILA";
case LWTUNNEL_ENCAP_SEG6:
return "SEG6";
case LWTUNNEL_ENCAP_IP6:
case LWTUNNEL_ENCAP_IP:
case LWTUNNEL_ENCAP_NONE:
Expand Down
12 changes: 12 additions & 0 deletions net/ipv6/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,16 @@ config IPV6_PIMSM_V2
Support for IPv6 PIM multicast routing protocol PIM-SMv2.
If unsure, say N.

config IPV6_SEG6_INLINE
bool "IPv6: direct Segment Routing Header insertion "
depends on IPV6
---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.

endif # IPV6
2 changes: 1 addition & 1 deletion net/ipv6/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,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
udp_offload.o seg6.o seg6_iptunnel.o

ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o

Expand Down
44 changes: 44 additions & 0 deletions net/ipv6/seg6.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,43 @@
#include <linux/seg6.h>
#include <linux/seg6_genl.h>

bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
{
int trailing;
unsigned int tlv_offset;

if (srh->type != IPV6_SRCRT_TYPE_4)
return false;

if (((srh->hdrlen + 1) << 3) != len)
return false;

if (srh->segments_left != srh->first_segment)
return false;

tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);

trailing = len - tlv_offset;
if (trailing < 0)
return false;

while (trailing) {
struct sr6_tlv *tlv;
unsigned int tlv_len;

tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
tlv_len = sizeof(*tlv) + tlv->len;

trailing -= tlv_len;
if (trailing < 0)
return false;

tlv_offset += tlv_len;
}

return true;
}

static struct genl_family seg6_genl_family;

static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
Expand Down Expand Up @@ -198,17 +235,24 @@ int __init seg6_init(void)
if (err)
goto out_unregister_genl;

err = seg6_iptunnel_init();
if (err)
goto out_unregister_pernet;

pr_info("Segment Routing with IPv6\n");

out:
return err;
out_unregister_pernet:
unregister_pernet_subsys(&ip6_segments_ops);
out_unregister_genl:
genl_unregister_family(&seg6_genl_family);
goto out;
}

void seg6_exit(void)
{
seg6_iptunnel_exit();
unregister_pernet_subsys(&ip6_segments_ops);
genl_unregister_family(&seg6_genl_family);
}
Loading

0 comments on commit 6c8702c

Please sign in to comment.