summaryrefslogtreecommitdiffstats
path: root/src/vnet/adj/adj_delegate.h
blob: d49c6661c192a1d8d347b5982982ad92b44e3d32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * A Delagate is a means to implement the Delagation design pattern;
 * the extension of an object's functionality through the composition of,
 * and delgation to, other objects.
 * These 'other' objects are delegates. Delagates are thus attached to
 * ADJ objects to extend their functionality.
 */

#ifndef __ADJ_DELEGATE_T__
#define __ADJ_DELEGATE_T__

#include <vnet/adj/adj.h>

/**
 * Built-in delegate types.
 * When adding new types, if your code is within the vnet subsystem, then add a
 * new  type here. If not then use the adj_delegate_register_new_type API to
 * register a new type.
 */
typedef enum adj_delegate_type_t_ {
    /**
     * BFD session state
     */
    ADJ_DELEGATE_BFD,
    /**
     * Stacking of a midchain's nexthop
     */
    ADJ_DELEGATE_MIDCHAIN,
} adj_delegate_type_t;

#define ADJ_DELEGATE_LAST (ADJ_DELEGATE_MIDCHAIN)

/**
 * Adj delegate. This object is attached to the adjacency.
 */
typedef struct adj_delegate_t_
{
    /**
     * The ADJ entry object to which the delagate is attached
     */
    adj_index_t ad_adj_index;

    /**
     * The delagate type
     */
    adj_delegate_type_t ad_type;

    /**
     * The index passed by the provider to identify its delegate instance.
     * As with all things VPP this is a pool index.
     */
    index_t ad_index;
} adj_delegate_t;

/**
 * Indication that the adjacency has been deleted. The delegate provider should free
 * the delegate.
 */
typedef void (*adj_delegate_adj_deleted_t)(adj_delegate_t *aed);

/**
 * Format function for the delegate
 */
typedef u8 * (*adj_delegate_format_t)(const adj_delegate_t *aed, u8 *s);

/**
 * Notification that an adjacency has been created
 */
typedef void (*adj_delegate_adj_created_t)(adj_index_t ai);

/**
 * An ADJ delegate virtual function table
 */
typedef struct adj_delegate_vft_t_ {
    adj_delegate_format_t adv_format;
    adj_delegate_adj_deleted_t adv_adj_deleted;
    adj_delegate_adj_created_t adv_adj_created;
} adj_delegate_vft_t;

/**
 * @brief Remove a delegate from an adjacency
 *
 * @param ai The adjacency to remove the delegate from
 * @param type The type of delegate being removed
 */
extern void adj_delegate_remove(adj_index_t ai,
                                adj_delegate_type_t type);

/**
 * @brief Add a delegate to an adjacency
 *
 * @param ai The adjacency to add the delegate to
 * @param type The type of delegate being added
 * @param adi The provider's [pool] index of its attached objet
 */
extern int adj_delegate_add(ip_adjacency_t *adj,
                            adj_delegate_type_t fdt,
                            index_t adi);

/**
 * @brief Get a delegate from an adjacency
 *
 * @param ai The adjacency to get the delegate from
 * @param type The type of delegate being sought
 */
extern adj_delegate_t *adj_delegate_get(const ip_adjacency_t *adj,
                                        adj_delegate_type_t type);

/**
 * @brief Register a VFT for one of the built-in types
 */
extern void adj_delegate_register_type(adj_delegate_type_t type,
                                       const adj_delegate_vft_t *vft);

/**
 * @brief create a new delegate type and register a new VFT
 */
extern adj_delegate_type_t adj_delegate_register_new_type(
    const adj_delegate_vft_t *vft);

#endif
super(TestDVR, self).setUp() self.create_pg_interfaces(range(4)) self.create_loopback_interfaces(1) for i in self.pg_interfaces: i.admin_up() self.loop0.config_ip4() def tearDown(self): for i in self.pg_interfaces: i.admin_down() self.loop0.unconfig_ip4() super(TestDVR, self).tearDown() def assert_same_mac_addr(self, tx, rx): t_eth = tx[Ether] for p in rx: r_eth = p[Ether] self.assertEqual(t_eth.src, r_eth.src) self.assertEqual(t_eth.dst, r_eth.dst) def assert_has_vlan_tag(self, tag, rx): for p in rx: r_1q = p[Dot1Q] self.assertEqual(tag, r_1q.vlan) def assert_has_no_tag(self, rx): for p in rx: self.assertFalse(p.haslayer(Dot1Q)) def test_dvr(self): """ Distributed Virtual Router """ # # A packet destined to an IP address that is L2 bridged via # a non-tag interface # ip_non_tag_bridged = "10.10.10.10" ip_tag_bridged = "10.10.10.11" any_src_addr = "1.1.1.1" pkt_no_tag = (Ether(src=self.pg0.remote_mac, dst=self.loop0.local_mac) / IP(src=any_src_addr, dst=ip_non_tag_bridged) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_tag = (Ether(src=self.pg0.remote_mac, dst=self.loop0.local_mac) / IP(src=any_src_addr, dst=ip_tag_bridged) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) # # Two sub-interfaces so we can test VLAN tag push/pop # sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92) sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93) sub_if_on_pg2.admin_up() sub_if_on_pg3.admin_up() # # Put all the interfaces into a new bridge domain # self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, port_type=L2_PORT_TYPE.BVI) self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index, L2_VTR_OP.L2_POP_1, 92) self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg3.sw_if_index, L2_VTR_OP.L2_POP_1, 93) # # Add routes to bridge the traffic via a tagged an nontagged interface # route_no_tag = VppIpRoute( self, ip_non_tag_bridged, 32, [VppRoutePath("0.0.0.0", self.pg1.sw_if_index, is_dvr=1)]) route_no_tag.add_vpp_config() # # Inject the packet that arrives and leaves on a non-tagged interface # Since it's 'bridged' expect that the MAC headed is unchanged. # rx = self.send_and_expect(self.pg0, pkt_no_tag * 65, self.pg1) self.assert_same_mac_addr(pkt_no_tag, rx) self.assert_has_no_tag(rx) # # Add routes to bridge the traffic via a tagged interface # route_with_tag = VppIpRoute( self, ip_tag_bridged, 32, [VppRoutePath("0.0.0.0", sub_if_on_pg3.sw_if_index, is_dvr=1)]) route_with_tag.add_vpp_config() # # Inject the packet that arrives non-tag and leaves on a tagged # interface # rx = self.send_and_expect(self.pg0, pkt_tag * 65, self.pg3) self.assert_same_mac_addr(pkt_tag, rx) self.assert_has_vlan_tag(93, rx) # # Tag to tag # pkt_tag_to_tag = (Ether(src=self.pg2.remote_mac, dst=self.loop0.local_mac) / Dot1Q(vlan=92) / IP(src=any_src_addr, dst=ip_tag_bridged) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) rx = self.send_and_expect(self.pg2, pkt_tag_to_tag * 65, self.pg3) self.assert_same_mac_addr(pkt_tag_to_tag, rx) self.assert_has_vlan_tag(93, rx) # # Tag to non-Tag # pkt_tag_to_non_tag = (Ether(src=self.pg2.remote_mac, dst=self.loop0.local_mac) / Dot1Q(vlan=92) / IP(src=any_src_addr, dst=ip_non_tag_bridged) / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) rx = self.send_and_expect(self.pg2, pkt_tag_to_non_tag * 65, self.pg1) self.assert_same_mac_addr(pkt_tag_to_tag, rx) self.assert_has_no_tag(rx) # # Add an output L3 ACL that will block the traffic # rule_1 = ({'is_permit': 0, 'is_ipv6': 0, 'proto': 17, 'srcport_or_icmptype_first': 1234, 'srcport_or_icmptype_last': 1234, 'src_ip_prefix_len': 32, 'src_ip_addr': inet_pton(AF_INET, any_src_addr), 'dstport_or_icmpcode_first': 1234, 'dstport_or_icmpcode_last': 1234, 'dst_ip_prefix_len': 32, 'dst_ip_addr': inet_pton(AF_INET, ip_non_tag_bridged)}) acl = self.vapi.acl_add_replace(acl_index=4294967295, r=[rule_1]) # # Apply the ACL on the output interface # self.vapi.acl_interface_set_acl_list(self.pg1.sw_if_index, 0, [acl.acl_index]) # # Send packet's that should match the ACL and be dropped # rx = self.send_and_assert_no_replies(self.pg2, pkt_tag_to_non_tag * 65) # # cleanup # self.vapi.acl_interface_set_acl_list(self.pg1.sw_if_index, 0, []) self.vapi.acl_del(acl.acl_index) self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index, 1, port_type=L2_PORT_TYPE.BVI, enable=0) # # Do a FIB dump to make sure the paths are correctly reported as DVR # routes = self.vapi.ip_fib_dump() for r in routes: if (inet_pton(AF_INET, ip_tag_bridged) == r.address): self.assertEqual(r.path[0].sw_if_index, sub_if_on_pg3.sw_if_index) self.assertEqual(r.path[0].is_dvr, 1) if (inet_pton(AF_INET, ip_non_tag_bridged) == r.address): self.assertEqual(r.path[0].sw_if_index, self.pg1.sw_if_index) self.assertEqual(r.path[0].is_dvr, 1) # # the explicit route delete is require so it happens before # the sbu-interface delete. subinterface delete is required # because that object type does not use the object registry # route_no_tag.remove_vpp_config() route_with_tag.remove_vpp_config() sub_if_on_pg3.remove_vpp_config() sub_if_on_pg2.remove_vpp_config() def test_l2_emulation(self): """ L2 Emulation """ # # non distinct L3 packets, in the tag/non-tag combos # pkt_no_tag = (Ether(src=self.pg0.remote_mac, dst=self.pg1.remote_mac) / IP(src="2.2.2.2", dst="1.1.1.1") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_to_tag = (Ether(src=self.pg0.remote_mac, dst=self.pg2.remote_mac) / IP(src="2.2.2.2", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_from_tag = (Ether(src=self.pg3.remote_mac, dst=self.pg2.remote_mac) / Dot1Q(vlan=93) / IP(src="2.2.2.2", dst="1.1.1.1") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_from_to_tag = (Ether(src=self.pg3.remote_mac, dst=self.pg2.remote_mac) / Dot1Q(vlan=93) / IP(src="2.2.2.2", dst="1.1.1.2") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) pkt_bcast = (Ether(src=self.pg0.remote_mac, dst="ff:ff:ff:ff:ff:ff") / IP(src="2.2.2.2", dst="255.255.255.255") / UDP(sport=1234, dport=1234) / Raw('\xa5' * 100)) # # A couple of sub-interfaces for tags # sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92) sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93) sub_if_on_pg2.admin_up() sub_if_on_pg3.admin_up() # # Put all the interfaces into a new bridge domain # self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1) self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index, L2_VTR_OP.L2_POP_1, 92) self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg3.sw_if_index, L2_VTR_OP.L2_POP_1, 93) # # Disable UU flooding, learning and ARP terminaation. makes this test # easier as unicast packets are dropped if not extracted. # self.vapi.bridge_flags(1, 0, (1 << 0) | (1 << 3) | (1 << 4)) # # Add a DVR route to steer traffic at L3 # route_1 = VppIpRoute(self, "1.1.1.1", 32, [VppRoutePath("0.0.0.0", self.pg1.sw_if_index, is_dvr=1)]) route_2 = VppIpRoute(self, "1.1.1.2", 32, [VppRoutePath("0.0.0.0", sub_if_on_pg2.sw_if_index, is_dvr=1)]) route_1.add_vpp_config() route_2.add_vpp_config() # # packets are dropped because bridge does not flood unknown unicast # self.send_and_assert_no_replies(self.pg0, pkt_no_tag) # # Enable L3 extraction on pgs # self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index) self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index) self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index) self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index) # # now we expect the packet forward according to the DVR route # rx = self.send_and_expect(self.pg0, pkt_no_tag * 65, self.pg1) self.assert_same_mac_addr(pkt_no_tag, rx) self.assert_has_no_tag(rx) rx = self.send_and_expect(self.pg0, pkt_to_tag * 65, self.pg2) self.assert_same_mac_addr(pkt_to_tag, rx) self.assert_has_vlan_tag(92, rx) rx = self.send_and_expect(self.pg3, pkt_from_tag * 65, self.pg1) self.assert_same_mac_addr(pkt_from_tag, rx) self.assert_has_no_tag(rx) rx = self.send_and_expect(self.pg3, pkt_from_to_tag * 65, self.pg2) self.assert_same_mac_addr(pkt_from_tag, rx) self.assert_has_vlan_tag(92, rx) # # but broadcast packets are still flooded # self.send_and_expect(self.pg0, pkt_bcast * 33, self.pg2) # # cleanup # self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index, enable=0) self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1, enable=0) self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1, enable=0) route_1.remove_vpp_config() route_2.remove_vpp_config() sub_if_on_pg3.remove_vpp_config() sub_if_on_pg2.remove_vpp_config() if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)