Commit e7f3863c authored by David S. Miller's avatar David S. Miller

Merge branch 'seg6.end.dt6'

Andrea Mayer says:

====================
seg6: add support for SRv6 End.DT46 Behavior

SRv6 End.DT46 Behavior is defined in the IETF RFC 8986 [1] along with SRv6
End.DT4 and End.DT6 Behaviors.

The proposed End.DT46 implementation is meant to support the decapsulation
of both IPv4 and IPv6 traffic coming from a *single* SRv6 tunnel.
The SRv6 End.DT46 Behavior greatly simplifies the setup and operations of
SRv6 VPNs in the Linux kernel.

 - patch 1/2 is the core patch that adds support for the SRv6 End.DT46
   Behavior;

 - patch 2/2 adds the selftest for SRv6 End.DT46 Behavior.

The patch introducing the new SRv6 End.DT46 Behavior in iproute2 will
follow shortly.

Comments, suggestions and improvements are very welcome as always!
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5a336f97 03a0b567
......@@ -64,6 +64,8 @@ enum {
SEG6_LOCAL_ACTION_END_AM = 14,
/* custom BPF action */
SEG6_LOCAL_ACTION_END_BPF = 15,
/* decap and lookup of DA in v4 or v6 table */
SEG6_LOCAL_ACTION_END_DT46 = 16,
__SEG6_LOCAL_ACTION_MAX,
};
......
......@@ -87,10 +87,10 @@ struct seg6_end_dt_info {
int vrf_ifindex;
int vrf_table;
/* tunneled packet proto and family (IPv4 or IPv6) */
__be16 proto;
/* tunneled packet family (IPv4 or IPv6).
* Protocol and header length are inferred from family.
*/
u16 family;
int hdrlen;
};
struct pcpu_seg6_local_counters {
......@@ -521,19 +521,6 @@ static int __seg6_end_dt_vrf_build(struct seg6_local_lwt *slwt, const void *cfg,
info->net = net;
info->vrf_ifindex = vrf_ifindex;
switch (family) {
case AF_INET:
info->proto = htons(ETH_P_IP);
info->hdrlen = sizeof(struct iphdr);
break;
case AF_INET6:
info->proto = htons(ETH_P_IPV6);
info->hdrlen = sizeof(struct ipv6hdr);
break;
default:
return -EINVAL;
}
info->family = family;
info->mode = DT_VRF_MODE;
......@@ -622,22 +609,44 @@ static struct net_device *end_dt_get_vrf_rcu(struct sk_buff *skb,
}
static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
struct seg6_local_lwt *slwt, u16 family)
{
struct seg6_end_dt_info *info = &slwt->dt_info;
struct net_device *vrf;
__be16 protocol;
int hdrlen;
vrf = end_dt_get_vrf_rcu(skb, info);
if (unlikely(!vrf))
goto drop;
skb->protocol = info->proto;
switch (family) {
case AF_INET:
protocol = htons(ETH_P_IP);
hdrlen = sizeof(struct iphdr);
break;
case AF_INET6:
protocol = htons(ETH_P_IPV6);
hdrlen = sizeof(struct ipv6hdr);
break;
case AF_UNSPEC:
fallthrough;
default:
goto drop;
}
if (unlikely(info->family != AF_UNSPEC && info->family != family)) {
pr_warn_once("seg6local: SRv6 End.DT* family mismatch");
goto drop;
}
skb->protocol = protocol;
skb_dst_drop(skb);
skb_set_transport_header(skb, info->hdrlen);
skb_set_transport_header(skb, hdrlen);
return end_dt_vrf_rcv(skb, info->family, vrf);
return end_dt_vrf_rcv(skb, family, vrf);
drop:
kfree_skb(skb);
......@@ -656,7 +665,7 @@ static int input_action_end_dt4(struct sk_buff *skb,
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto drop;
skb = end_dt_vrf_core(skb, slwt);
skb = end_dt_vrf_core(skb, slwt, AF_INET);
if (!skb)
/* packet has been processed and consumed by the VRF */
return 0;
......@@ -739,7 +748,7 @@ static int input_action_end_dt6(struct sk_buff *skb,
goto legacy_mode;
/* DT6_VRF_MODE */
skb = end_dt_vrf_core(skb, slwt);
skb = end_dt_vrf_core(skb, slwt, AF_INET6);
if (!skb)
/* packet has been processed and consumed by the VRF */
return 0;
......@@ -767,6 +776,36 @@ static int input_action_end_dt6(struct sk_buff *skb,
return -EINVAL;
}
#ifdef CONFIG_NET_L3_MASTER_DEV
static int seg6_end_dt46_build(struct seg6_local_lwt *slwt, const void *cfg,
struct netlink_ext_ack *extack)
{
return __seg6_end_dt_vrf_build(slwt, cfg, AF_UNSPEC, extack);
}
static int input_action_end_dt46(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
unsigned int off = 0;
int nexthdr;
nexthdr = ipv6_find_hdr(skb, &off, -1, NULL, NULL);
if (unlikely(nexthdr < 0))
goto drop;
switch (nexthdr) {
case IPPROTO_IPIP:
return input_action_end_dt4(skb, slwt);
case IPPROTO_IPV6:
return input_action_end_dt6(skb, slwt);
}
drop:
kfree_skb(skb);
return -EINVAL;
}
#endif
/* push an SRH on top of the current one */
static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{
......@@ -968,6 +1007,17 @@ static struct seg6_action_desc seg6_action_table[] = {
#endif
.input = input_action_end_dt6,
},
{
.action = SEG6_LOCAL_ACTION_END_DT46,
.attrs = SEG6_F_ATTR(SEG6_LOCAL_VRFTABLE),
.optattrs = SEG6_F_LOCAL_COUNTERS,
#ifdef CONFIG_NET_L3_MASTER_DEV
.input = input_action_end_dt46,
.slwt_ops = {
.build_state = seg6_end_dt46_build,
},
#endif
},
{
.action = SEG6_LOCAL_ACTION_END_B6,
.attrs = SEG6_F_ATTR(SEG6_LOCAL_SRH),
......
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