Skip to content

Commit

Permalink
Merge branch 'mpls-in-ipv4-and-udp'
Browse files Browse the repository at this point in the history
Simon Horman says:

====================
net: support MPLS in IPv4 and UDP

This short series provides support for MPLS in IPv4 (RFC4023), and by
virtue of FOU, MPLS in UDP (RFC7510).

The changes are as follows:
1. Teach tunnel4.c about AF_MPLS, it already understands AF_INET and
   AF_INET6
2. Enhance IPIP and SIT to handle MPLS. Both already handle IPv4.
   SIT also already handles IPv6.
3. Trivially enhance MPLS to allow routes over SIT and IPIP tunnels.

A corresponding patch set for iproute2 has also been provided.

Changes since v1
* Correct inverted IPIP protocol logic in SIT patch
* Provide usage example below

Sample configuration follows:

* The following creates a tunnel and routes MPLS packets whose outermost
  label is 100 over it. The forwarded packets will have the outermost label
  stack entry, 100, removed and two label stack entries added, the
  outermost having label 200 and the next having label 300.

  The local end-point for the tunnel is 10.0.99.192 and the remote
  endpoint is 10.0.99.193.

  The local address for encapsulated packets is 10.0.98.192 and the
  remote address is 10.0.98.193.

  # Create an MPLS over IPv4 tunnel using the IPIP driver
  ip link add name tun1 type ipip remote 10.0.99.193 local 10.0.99.192 \
	ttl 225 mode mplsip

  # Bring the tunnel up and an add an IPv4 address and route
  ip link set up dev tun1
  ip addr add 10.0.98.192/24 dev tun1

  # Set MPLS route
  # Allow MPLS forwarding of packets recieved on eth0
  echo 1 > /proc/sys/net/mpls/conf/eth0/input
  # Larger than label to be routed (100)
  echo 101 > /proc/sys/net/mpls/platform_labels
  ip -f mpls route add 100 as 200/300 via inet 10.0.98.193

* For FOU (in this case MPLS over UDP) a tunnel may created using:

  # Packets recieved on UDP port 6635 are MPLS over UDP (IP proto 137)
  ip fou add port 6635 ipproto 137
  # Create the tunnel netdev
  ip link add name tun1 type ipip remote 10.0.99.193 local 10.0.99.192 \
	ttl 225 mode mplsip encap fou encap-sport auto encap-dport 6635

  IPv4 address, link and route, and MPLS routing commands are as per
  the MPLS over IPv4 example

* To use the SIT driver instead of the IPIP driver "ipip" may be substituted
  for "sit" in the above examples.

* To create a tunnel that forwards and receives all supported
  inner-protocols "mplsip" may be substituted for "any" in the above
  examples.

  For the IPIP driver this configures both IPv4 and MPLS over IPv4.
  For the SIT driver this configures IPv6, IPv4 and MPLS over IPv4.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 9, 2016
2 parents a65056e + 407f31b commit fb57731
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 44 deletions.
137 changes: 121 additions & 16 deletions net/ipv4/ipip.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ static int ipip_err(struct sk_buff *skb, u32 info)

if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
ipv4_update_pmtu(skb, dev_net(skb->dev), info,
t->parms.link, 0, IPPROTO_IPIP, 0);
t->parms.link, 0, iph->protocol, 0);
err = 0;
goto out;
}

if (type == ICMP_REDIRECT) {
ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
IPPROTO_IPIP, 0);
iph->protocol, 0);
err = 0;
goto out;
}
Expand All @@ -177,12 +177,19 @@ static int ipip_err(struct sk_buff *skb, u32 info)
return err;
}

static const struct tnl_ptk_info tpi = {
static const struct tnl_ptk_info ipip_tpi = {
/* no tunnel info required for ipip. */
.proto = htons(ETH_P_IP),
};

static int ipip_rcv(struct sk_buff *skb)
#if IS_ENABLED(CONFIG_MPLS)
static const struct tnl_ptk_info mplsip_tpi = {
/* no tunnel info required for mplsip. */
.proto = htons(ETH_P_MPLS_UC),
};
#endif

static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto)
{
struct net *net = dev_net(skb->dev);
struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
Expand All @@ -193,11 +200,23 @@ static int ipip_rcv(struct sk_buff *skb)
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->saddr, iph->daddr, 0);
if (tunnel) {
const struct tnl_ptk_info *tpi;

if (tunnel->parms.iph.protocol != ipproto &&
tunnel->parms.iph.protocol != 0)
goto drop;

if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
if (iptunnel_pull_header(skb, 0, tpi.proto, false))
#if IS_ENABLED(CONFIG_MPLS)
if (ipproto == IPPROTO_MPLS)
tpi = &mplsip_tpi;
else
#endif
tpi = &ipip_tpi;
if (iptunnel_pull_header(skb, 0, tpi->proto, false))
goto drop;
return ip_tunnel_rcv(tunnel, skb, &tpi, NULL, log_ecn_error);
return ip_tunnel_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
}

return -1;
Expand All @@ -207,24 +226,51 @@ static int ipip_rcv(struct sk_buff *skb)
return 0;
}

static int ipip_rcv(struct sk_buff *skb)
{
return ipip_tunnel_rcv(skb, IPPROTO_IPIP);
}

#if IS_ENABLED(CONFIG_MPLS)
static int mplsip_rcv(struct sk_buff *skb)
{
return ipip_tunnel_rcv(skb, IPPROTO_MPLS);
}
#endif

/*
* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
*/
static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
const struct iphdr *tiph = &tunnel->parms.iph;
u8 ipproto;

switch (skb->protocol) {
case htons(ETH_P_IP):
ipproto = IPPROTO_IPIP;
break;
#if IS_ENABLED(CONFIG_MPLS)
case htons(ETH_P_MPLS_UC):
ipproto = IPPROTO_MPLS;
break;
#endif
default:
goto tx_error;
}

if (unlikely(skb->protocol != htons(ETH_P_IP)))
if (tiph->protocol != ipproto && tiph->protocol != 0)
goto tx_error;

if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4))
goto tx_error;

skb_set_inner_ipproto(skb, IPPROTO_IPIP);
skb_set_inner_ipproto(skb, ipproto);

ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
ip_tunnel_xmit(skb, dev, tiph, ipproto);
return NETDEV_TX_OK;

tx_error:
Expand All @@ -234,6 +280,20 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}

static bool ipip_tunnel_ioctl_verify_protocol(u8 ipproto)
{
switch (ipproto) {
case 0:
case IPPROTO_IPIP:
#if IS_ENABLED(CONFIG_MPLS)
case IPPROTO_MPLS:
#endif
return true;
}

return false;
}

static int
ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
Expand All @@ -244,7 +304,8 @@ ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EFAULT;

if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
if (p.iph.version != 4 ||
!ipip_tunnel_ioctl_verify_protocol(p.iph.protocol) ||
p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
return -EINVAL;
}
Expand Down Expand Up @@ -301,10 +362,23 @@ static int ipip_tunnel_init(struct net_device *dev)

tunnel->tun_hlen = 0;
tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
tunnel->parms.iph.protocol = IPPROTO_IPIP;
return ip_tunnel_init(dev);
}

static int ipip_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
{
u8 proto;

if (!data || !data[IFLA_IPTUN_PROTO])
return 0;

proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
if (proto != IPPROTO_IPIP && proto != IPPROTO_MPLS && proto != 0)
return -EINVAL;

return 0;
}

static void ipip_netlink_parms(struct nlattr *data[],
struct ip_tunnel_parm *parms)
{
Expand Down Expand Up @@ -335,6 +409,9 @@ static void ipip_netlink_parms(struct nlattr *data[],
if (data[IFLA_IPTUN_TOS])
parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);

if (data[IFLA_IPTUN_PROTO])
parms->iph.protocol = nla_get_u8(data[IFLA_IPTUN_PROTO]);

if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
parms->iph.frag_off = htons(IP_DF);
}
Expand Down Expand Up @@ -427,6 +504,8 @@ static size_t ipip_get_size(const struct net_device *dev)
nla_total_size(1) +
/* IFLA_IPTUN_TOS */
nla_total_size(1) +
/* IFLA_IPTUN_PROTO */
nla_total_size(1) +
/* IFLA_IPTUN_PMTUDISC */
nla_total_size(1) +
/* IFLA_IPTUN_ENCAP_TYPE */
Expand All @@ -450,6 +529,7 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) ||
nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
!!(parm->iph.frag_off & htons(IP_DF))))
goto nla_put_failure;
Expand All @@ -476,6 +556,7 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
[IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
[IFLA_IPTUN_TTL] = { .type = NLA_U8 },
[IFLA_IPTUN_TOS] = { .type = NLA_U8 },
[IFLA_IPTUN_PROTO] = { .type = NLA_U8 },
[IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
[IFLA_IPTUN_ENCAP_TYPE] = { .type = NLA_U16 },
[IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 },
Expand All @@ -489,6 +570,7 @@ static struct rtnl_link_ops ipip_link_ops __read_mostly = {
.policy = ipip_policy,
.priv_size = sizeof(struct ip_tunnel),
.setup = ipip_tunnel_setup,
.validate = ipip_tunnel_validate,
.newlink = ipip_newlink,
.changelink = ipip_changelink,
.dellink = ip_tunnel_dellink,
Expand All @@ -503,6 +585,14 @@ static struct xfrm_tunnel ipip_handler __read_mostly = {
.priority = 1,
};

#if IS_ENABLED(CONFIG_MPLS)
static struct xfrm_tunnel mplsip_handler __read_mostly = {
.handler = mplsip_rcv,
.err_handler = ipip_err,
.priority = 1,
};
#endif

static int __net_init ipip_init_net(struct net *net)
{
return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");
Expand All @@ -525,16 +615,23 @@ static int __init ipip_init(void)
{
int err;

pr_info("ipip: IPv4 over IPv4 tunneling driver\n");
pr_info("ipip: IPv4 and MPLS over IPv4 tunneling driver\n");

err = register_pernet_device(&ipip_net_ops);
if (err < 0)
return err;
err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
if (err < 0) {
pr_info("%s: can't register tunnel\n", __func__);
goto xfrm_tunnel_failed;
goto xfrm_tunnel_ipip_failed;
}
#if IS_ENABLED(CONFIG_MPLS)
err = xfrm4_tunnel_register(&mplsip_handler, AF_MPLS);
if (err < 0) {
pr_info("%s: can't register tunnel\n", __func__);
goto xfrm_tunnel_mplsip_failed;
}
#endif
err = rtnl_link_register(&ipip_link_ops);
if (err < 0)
goto rtnl_link_failed;
Expand All @@ -543,8 +640,13 @@ static int __init ipip_init(void)
return err;

rtnl_link_failed:
#if IS_ENABLED(CONFIG_MPLS)
xfrm4_tunnel_deregister(&mplsip_handler, AF_INET);
xfrm_tunnel_mplsip_failed:

#endif
xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
xfrm_tunnel_failed:
xfrm_tunnel_ipip_failed:
unregister_pernet_device(&ipip_net_ops);
goto out;
}
Expand All @@ -554,7 +656,10 @@ static void __exit ipip_fini(void)
rtnl_link_unregister(&ipip_link_ops);
if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
pr_info("%s: can't deregister tunnel\n", __func__);

#if IS_ENABLED(CONFIG_MPLS)
if (xfrm4_tunnel_deregister(&mplsip_handler, AF_MPLS))
pr_info("%s: can't deregister tunnel\n", __func__);
#endif
unregister_pernet_device(&ipip_net_ops);
}

Expand Down
Loading

0 comments on commit fb57731

Please sign in to comment.