From e294de6f876587ddc34ab02771771aea60087adc Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 21 Dec 2020 09:52:24 +0000 Subject: [PATCH] ipip: Support MPLS over IP Type: feature Signed-off-by: Neale Ranns Change-Id: Ief1755131297afcaa14fe74fd8dd28c71a92fbe6 --- src/vnet/ipip/ipip.c | 52 +++++++++++++++ src/vnet/ipip/node.c | 22 +++++-- src/vnet/tunnel/tunnel_dp.h | 33 ++++++++++ test/test_ipip.py | 150 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 251 insertions(+), 6 deletions(-) diff --git a/src/vnet/ipip/ipip.c b/src/vnet/ipip/ipip.c index 357eb58f160..ca52142f5c6 100644 --- a/src/vnet/ipip/ipip.c +++ b/src/vnet/ipip/ipip.c @@ -93,6 +93,9 @@ ipip_build_rewrite (vnet_main_t * vnm, u32 sw_if_index, case VNET_LINK_IP4: ip4->protocol = IP_PROTOCOL_IP_IN_IP; break; + case VNET_LINK_MPLS: + ip4->protocol = IP_PROTOCOL_MPLS_IN_IP; + break; default: break; } @@ -121,6 +124,9 @@ ipip_build_rewrite (vnet_main_t * vnm, u32 sw_if_index, case VNET_LINK_IP4: ip6->protocol = IP_PROTOCOL_IP_IN_IP; break; + case VNET_LINK_MPLS: + ip6->protocol = IP_PROTOCOL_MPLS_IN_IP; + break; default: break; } @@ -201,6 +207,46 @@ ipip66_fixup (vlib_main_t * vm, tunnel_encap_fixup_6o6 (flags, ip6 + 1, ip6); } +static void +ipipm6_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b, + const void *data) +{ + tunnel_encap_decap_flags_t flags; + ip6_header_t *ip6; + + flags = pointer_to_uword (data); + + /* Must set locally originated otherwise we're not allowed to + fragment the packet later and we'll get an unwanted hop-limt + decrement */ + b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + + ip6 = vlib_buffer_get_current (b); + ip6->payload_length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) - sizeof (*ip6)); + tunnel_encap_fixup_mplso6 (flags, (mpls_unicast_header_t *) (ip6 + 1), ip6); +} + +static void +ipipm4_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b, + const void *data) +{ + tunnel_encap_decap_flags_t flags; + ip4_header_t *ip4; + + flags = pointer_to_uword (data); + + /* Must set locally originated otherwise we'll do a TTL decrement + * during ip4-rewrite */ + b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + + ip4 = vlib_buffer_get_current (b); + ip4->length = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) - sizeof (*ip4)); + tunnel_encap_fixup_mplso4 (flags, (mpls_unicast_header_t *) (ip4 + 1), ip4); + ip4->checksum = ip4_header_checksum (ip4); +} + static void ipip_tunnel_stack (adj_index_t ai) { @@ -265,8 +311,12 @@ ipip_get_fixup (const ipip_tunnel_t * t, vnet_link_t lt, adj_flags_t * aflags) return (ipip66_fixup); if (t->transport == IPIP_TRANSPORT_IP6 && lt == VNET_LINK_IP4) return (ipip46_fixup); + if (t->transport == IPIP_TRANSPORT_IP6 && lt == VNET_LINK_MPLS) + return (ipipm6_fixup); if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_IP6) return (ipip64_fixup); + if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_MPLS) + return (ipipm4_fixup); if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_IP4) { *aflags = *aflags | ADJ_FLAG_MIDCHAIN_FIXUP_IP4O4_HDR; @@ -756,12 +806,14 @@ ipip_add_tunnel (ipip_transport_t transport, if (t->transport == IPIP_TRANSPORT_IP6 && !gm->ip6_protocol_registered) { ip6_register_protocol (IP_PROTOCOL_IP_IN_IP, ipip6_input_node.index); + ip6_register_protocol (IP_PROTOCOL_MPLS_IN_IP, ipip6_input_node.index); ip6_register_protocol (IP_PROTOCOL_IPV6, ipip6_input_node.index); gm->ip6_protocol_registered = true; } else if (t->transport == IPIP_TRANSPORT_IP4 && !gm->ip4_protocol_registered) { ip4_register_protocol (IP_PROTOCOL_IP_IN_IP, ipip4_input_node.index); + ip4_register_protocol (IP_PROTOCOL_MPLS_IN_IP, ipip4_input_node.index); ip4_register_protocol (IP_PROTOCOL_IPV6, ipip4_input_node.index); gm->ip4_protocol_registered = true; } diff --git a/src/vnet/ipip/node.c b/src/vnet/ipip/node.c index baaeee863fc..b008a21a20f 100644 --- a/src/vnet/ipip/node.c +++ b/src/vnet/ipip/node.c @@ -22,11 +22,12 @@ #include #include -#define foreach_ipip_input_next \ - _(PUNT, "error-punt") \ - _(DROP, "error-drop") \ - _(IP4_INPUT, "ip4-input") \ - _(IP6_INPUT, "ip6-input") +#define foreach_ipip_input_next \ + _ (PUNT, "error-punt") \ + _ (DROP, "error-drop") \ + _ (IP4_INPUT, "ip4-input") \ + _ (IP6_INPUT, "ip6-input") \ + _ (MPLS_INPUT, "mpls-input") typedef enum { @@ -175,6 +176,17 @@ ipip_input (vlib_main_t * vm, vlib_node_runtime_t * node, else tunnel_decap_fixup_4o4 (t0->flags, ip40 + 1, ip40); } + else if (inner_protocol0 == IP_PROTOCOL_MPLS_IN_IP) + { + next0 = IPIP_INPUT_NEXT_MPLS_INPUT; + + if (is_ipv6) + tunnel_decap_fixup_mplso6 ( + t0->flags, (mpls_unicast_header_t *) (ip60 + 1), ip60); + else + tunnel_decap_fixup_mplso4 ( + t0->flags, (mpls_unicast_header_t *) ip40 + 1, ip40); + } if (!is_ipv6 && t0->mode == IPIP_MODE_6RD && t0->sixrd.security_check) diff --git a/src/vnet/tunnel/tunnel_dp.h b/src/vnet/tunnel/tunnel_dp.h index f84e764f7ea..d6f07b5ec21 100644 --- a/src/vnet/tunnel/tunnel_dp.h +++ b/src/vnet/tunnel/tunnel_dp.h @@ -120,6 +120,25 @@ tunnel_encap_fixup_4o6 (tunnel_encap_decap_flags_t flags, ip6_set_ecn_network_order (outer, ip4_header_get_ecn (inner)); } +static_always_inline void +tunnel_encap_fixup_mplso6 (tunnel_encap_decap_flags_t flags, + const mpls_unicast_header_t *inner, + ip6_header_t *outer) +{ + if (flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP) + ip6_set_dscp_network_order (outer, + vnet_mpls_uc_get_exp (inner->label_exp_s_ttl)); +} + +static_always_inline void +tunnel_encap_fixup_mplso4 (tunnel_encap_decap_flags_t flags, + const mpls_unicast_header_t *inner, + ip4_header_t *outer) +{ + if (flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP) + ip4_header_set_dscp (outer, vnet_mpls_uc_get_exp (inner->label_exp_s_ttl)); +} + static_always_inline void tunnel_decap_fixup_4o6 (tunnel_encap_decap_flags_t flags, ip4_header_t * inner, const ip6_header_t * outer) @@ -152,6 +171,20 @@ tunnel_decap_fixup_4o4 (tunnel_encap_decap_flags_t flags, ip4_header_set_ecn_w_chksum (inner, ip4_header_get_ecn (outer)); } +static_always_inline void +tunnel_decap_fixup_mplso6 (tunnel_encap_decap_flags_t flags, + mpls_unicast_header_t *inner, + const ip6_header_t *outer) +{ +} + +static_always_inline void +tunnel_decap_fixup_mplso4 (tunnel_encap_decap_flags_t flags, + mpls_unicast_header_t *inner, + const ip4_header_t *outer) +{ +} + #endif /* diff --git a/test/test_ipip.py b/test/test_ipip.py index 8f18c07341d..83395e0bd72 100644 --- a/test/test_ipip.py +++ b/test/test_ipip.py @@ -3,10 +3,12 @@ import unittest from scapy.layers.inet6 import IPv6, Ether, IP, UDP, IPv6ExtHdrFragment, Raw +from scapy.contrib.mpls import MPLS from scapy.all import fragment, fragment6, RandShort, defragment6 from framework import VppTestCase, VppTestRunner from vpp_ip import DpoProto -from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto +from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto, \ + VppMplsLabel, VppMplsRoute, VppMplsTable from vpp_ipip_tun_interface import VppIpIpTunInterface from vpp_teib import VppTeib from vpp_papi import VppEnum @@ -1167,5 +1169,151 @@ class TestIPIP6(VppTestCase): return 'x' * len +class TestMPLS(VppTestCase): + """ MPLS Test Case """ + + @classmethod + def setUpClass(cls): + super(TestMPLS, cls).setUpClass() + cls.create_pg_interfaces(range(2)) + cls.interfaces = list(cls.pg_interfaces) + + @classmethod + def tearDownClass(cls): + super(TestMPLS, cls).tearDownClass() + + def setUp(self): + super(TestMPLS, self).setUp() + for i in self.interfaces: + i.admin_up() + i.config_ip4() + i.config_ip6() + i.disable_ipv6_ra() + i.resolve_arp() + i.resolve_ndp() + + def tearDown(self): + super(TestMPLS, self).tearDown() + + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + i.admin_down() + + def test_mpls(self): + """ MPLS over ip{6,4} test """ + + tbl = VppMplsTable(self, 0) + tbl.add_vpp_config() + + self.p_ether = Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) + self.p_payload = UDP(sport=1234, dport=1234) / Raw(b'X' * 100) + f = FibPathProto + + # IPv4 transport + tun4 = VppIpIpTunInterface( + self, + self.pg1, + self.pg1.local_ip4, + self.pg1.remote_ip4).add_vpp_config() + tun4.admin_up() + tun4.config_ip4() + tun4.enable_mpls() + + # IPv6 transport + tun6 = VppIpIpTunInterface( + self, + self.pg1, + self.pg1.local_ip6, + self.pg1.remote_ip6).add_vpp_config() + tun6.admin_up() + tun6.config_ip6() + tun6.enable_mpls() + + # ip routes into the tunnels with output labels + r4 = VppIpRoute(self, "1.1.1.1", 32, + [VppRoutePath( + tun4.remote_ip4, + tun4.sw_if_index, + labels=[VppMplsLabel(44)])]).add_vpp_config() + r6 = VppIpRoute(self, "1::1", 128, + [VppRoutePath( + tun6.remote_ip6, + tun6.sw_if_index, + labels=[VppMplsLabel(66)])]).add_vpp_config() + + # deag MPLS routes from the tunnel + r4 = VppMplsRoute(self, 44, 1, + [VppRoutePath( + self.pg0.remote_ip4, + self.pg0.sw_if_index)]).add_vpp_config() + r6 = VppMplsRoute(self, 66, 1, + [VppRoutePath( + self.pg0.remote_ip6, + self.pg0.sw_if_index)], + eos_proto=f.FIB_PATH_NH_PROTO_IP6).add_vpp_config() + + # + # Tunnel Encap + # + p4 = (self.p_ether / IP(src="2.2.2.2", dst="1.1.1.1") / self.p_payload) + + rxs = self.send_and_expect(self.pg0, p4 * N_PACKETS, self.pg1) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg1.local_ip4) + self.assertEqual(rx[IP].dst, self.pg1.remote_ip4) + self.assertEqual(rx[MPLS].label, 44) + inner = rx[MPLS].payload + self.assertEqual(inner.src, "2.2.2.2") + self.assertEqual(inner.dst, "1.1.1.1") + + p6 = (self.p_ether / IPv6(src="2::2", dst="1::1") / self.p_payload) + + rxs = self.send_and_expect(self.pg0, p6 * N_PACKETS, self.pg1) + + for rx in rxs: + self.assertEqual(rx[IPv6].src, self.pg1.local_ip6) + self.assertEqual(rx[IPv6].dst, self.pg1.remote_ip6) + self.assertEqual(rx[MPLS].label, 66) + inner = rx[MPLS].payload + self.assertEqual(inner.src, "2::2") + self.assertEqual(inner.dst, "1::1") + + # + # Tunnel Decap + # + p4 = (self.p_ether / + IP(src=self.pg1.remote_ip4, + dst=self.pg1.local_ip4) / + MPLS(label=44, ttl=4) / + IP(src="1.1.1.1", + dst="2.2.2.2") / + self.p_payload) + + rxs = self.send_and_expect(self.pg1, p4 * N_PACKETS, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].src, "1.1.1.1") + self.assertEqual(rx[IP].dst, "2.2.2.2") + + p6 = (self.p_ether / + IPv6(src=self.pg1.remote_ip6, + dst=self.pg1.local_ip6) / + MPLS(label=66, ttl=4) / + IPv6(src="1::1", + dst="2::2") / + self.p_payload) + + rxs = self.send_and_expect(self.pg1, p6 * N_PACKETS, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IPv6].src, "1::1") + self.assertEqual(rx[IPv6].dst, "2::2") + + tun4.disable_mpls() + tun6.disable_mpls() + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) -- 2.16.6