blob: d9900c2ffb676b5730db384ae65a145502bf66c7 [file] [log] [blame]
Charles Chan2e2e3402017-06-19 14:00:53 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Charles Chan2e2e3402017-06-19 14:00:53 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.segmentrouting;
18
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
22import org.junit.Before;
23import org.junit.Test;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.IpPrefix;
26import org.onlab.packet.MacAddress;
27import org.onlab.packet.VlanId;
28import org.onosproject.core.DefaultApplicationId;
29import org.onosproject.incubator.net.intf.Interface;
30import org.onosproject.incubator.net.intf.InterfaceServiceAdapter;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultHost;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.Host;
35import org.onosproject.net.HostId;
36import org.onosproject.net.HostLocation;
37import org.onosproject.net.PortNumber;
38import org.onosproject.net.config.NetworkConfigRegistryAdapter;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flow.criteria.Criterion;
42import org.onosproject.net.flow.criteria.EthCriterion;
43import org.onosproject.net.flow.criteria.VlanIdCriterion;
44import org.onosproject.net.flow.instructions.Instruction;
45import org.onosproject.net.flow.instructions.Instructions;
46import org.onosproject.net.flow.instructions.L2ModificationInstruction;
47import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
48import org.onosproject.net.flowobjective.ForwardingObjective;
49import org.onosproject.net.flowobjective.Objective;
50import org.onosproject.net.host.HostEvent;
Charles Chanf9a52702017-06-16 15:19:24 -070051import org.onosproject.net.host.HostServiceAdapter;
Charles Chan2e2e3402017-06-19 14:00:53 -070052import org.onosproject.net.host.InterfaceIpAddress;
53import org.onosproject.net.provider.ProviderId;
54import org.onosproject.segmentrouting.config.DeviceConfiguration;
55
56import java.util.Map;
57import java.util.Objects;
58import java.util.Set;
59import java.util.concurrent.atomic.AtomicInteger;
60
61import static org.junit.Assert.*;
62
63/**
64 * Unit test for {@link HostHandler}.
65 */
66public class HostHandlerTest {
67 private SegmentRoutingManager srManager;
68 private HostHandler hostHandler;
69
70 // Mocked routing and bridging tables
71 private Map<BridingTableKey, BridingTableValue> bridgingTable = Maps.newConcurrentMap();
72 private Map<RoutingTableKey, RoutingTableValue> routingTable = Maps.newConcurrentMap();
73 // Mocked Next Id
74 private Map<Integer, TrafficTreatment> nextTable = Maps.newConcurrentMap();
75 private AtomicInteger atomicNextId = new AtomicInteger();
76
Charles Chanf9a52702017-06-16 15:19:24 -070077 // Host Mac, VLAN
Charles Chan2e2e3402017-06-19 14:00:53 -070078 private static final ProviderId PROVIDER_ID = ProviderId.NONE;
79 private static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
80 private static final VlanId HOST_VLAN_UNTAGGED = VlanId.NONE;
81 private static final HostId HOST_ID_UNTAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_UNTAGGED);
82 private static final VlanId HOST_VLAN_TAGGED = VlanId.vlanId((short) 20);
83 private static final HostId HOST_ID_TAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_TAGGED);
Charles Chanf9a52702017-06-16 15:19:24 -070084 // Host IP
85 private static final IpAddress HOST_IP11 = IpAddress.valueOf("10.0.1.1");
86 private static final IpAddress HOST_IP21 = IpAddress.valueOf("10.0.2.1");
87 private static final IpAddress HOST_IP12 = IpAddress.valueOf("10.0.1.2");
88 private static final IpAddress HOST_IP13 = IpAddress.valueOf("10.0.1.3");
89 private static final IpAddress HOST_IP14 = IpAddress.valueOf("10.0.1.4");
90 // Device
91 private static final DeviceId DEV1 = DeviceId.deviceId("of:0000000000000001");
92 private static final DeviceId DEV2 = DeviceId.deviceId("of:0000000000000002");
93 // Port
94 private static final PortNumber P1 = PortNumber.portNumber(1);
95 private static final PortNumber P2 = PortNumber.portNumber(2);
96 private static final PortNumber P3 = PortNumber.portNumber(3);
97 // Connect Point
98 private static final ConnectPoint CP11 = new ConnectPoint(DEV1, P1);
99 private static final HostLocation HOST_LOC11 = new HostLocation(CP11, 0);
100 private static final ConnectPoint CP12 = new ConnectPoint(DEV1, P2);
101 private static final HostLocation HOST_LOC12 = new HostLocation(CP12, 0);
102 private static final ConnectPoint CP13 = new ConnectPoint(DEV1, P3);
103 private static final HostLocation HOST_LOC13 = new HostLocation(CP13, 0);
104 private static final ConnectPoint CP21 = new ConnectPoint(DEV2, P1);
105 private static final HostLocation HOST_LOC21 = new HostLocation(CP21, 0);
106 private static final ConnectPoint CP22 = new ConnectPoint(DEV2, P2);
107 private static final HostLocation HOST_LOC22 = new HostLocation(CP22, 0);
108 // Interface VLAN
Charles Chan2e2e3402017-06-19 14:00:53 -0700109 private static final VlanId INTF_VLAN_UNTAGGED = VlanId.vlanId((short) 10);
Charles Chan2e2e3402017-06-19 14:00:53 -0700110 private static final Set<VlanId> INTF_VLAN_TAGGED = Sets.newHashSet(VlanId.vlanId((short) 20));
111 private static final VlanId INTF_VLAN_NATIVE = VlanId.vlanId((short) 30);
Charles Chanf9a52702017-06-16 15:19:24 -0700112 // Interface subnet
113 private static final IpPrefix INTF_PREFIX1 = IpPrefix.valueOf("10.0.1.254/24");
114 private static final IpPrefix INTF_PREFIX2 = IpPrefix.valueOf("10.0.2.254/24");
115 private static final InterfaceIpAddress INTF_IP1 =
116 new InterfaceIpAddress(INTF_PREFIX1.address(), INTF_PREFIX1);
117 private static final InterfaceIpAddress INTF_IP2 =
118 new InterfaceIpAddress(INTF_PREFIX2.address(), INTF_PREFIX2);
119 // Host
120 private static final Host HOST1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC,
121 HOST_VLAN_UNTAGGED, Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11),
122 false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700123
124 @Before
125 public void setUp() throws Exception {
126 srManager = new MockSegmentRoutingManager();
127 srManager.cfgService = new NetworkConfigRegistryAdapter();
128 srManager.deviceConfiguration = new DeviceConfiguration(srManager);
129 srManager.flowObjectiveService = new MockFlowObjectiveService();
130 srManager.routingRulePopulator = new MockRoutingRulePopulator();
131 srManager.interfaceService = new MockInterfaceService();
Charles Chanf9a52702017-06-16 15:19:24 -0700132 srManager.hostService = new MockHostService();
Charles Chan2e2e3402017-06-19 14:00:53 -0700133
134 hostHandler = new HostHandler(srManager);
135
136 routingTable.clear();
137 bridgingTable.clear();
138 }
139
140 @Test
141 public void init() throws Exception {
Charles Chanf9a52702017-06-16 15:19:24 -0700142 hostHandler.init(DEV1);
143 assertEquals(1, routingTable.size());
144 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
145 assertEquals(1, bridgingTable.size());
146 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
147
148 hostHandler.init(DEV2);
149 assertEquals(2, routingTable.size());
150 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
151 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
152 assertEquals(2, bridgingTable.size());
153 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
154 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
155 }
156
157 @Test(expected = IllegalArgumentException.class)
158 public void testHostAddedAtWrongLocation() throws Exception {
159 hostHandler.processHostAddedAtLocation(HOST1, HOST_LOC13);
160 }
161
162
163 @Test()
164 public void testHostAddedAtCorrectLocation() throws Exception {
165 hostHandler.processHostAddedAtLocation(HOST1, HOST_LOC11);
166 assertEquals(1, routingTable.size());
167 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
168 assertEquals(1, bridgingTable.size());
169 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700170 }
171
172 @Test
173 public void testHostAdded() throws Exception {
174 Host subject;
175
176 // Untagged host discovered on untagged port
177 // Expect: add one routing rule and one bridging rule
178 subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700179 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700180 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
181 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700182 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700183 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700184 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700185
186 // Untagged host discovered on tagged/native port
187 // Expect: add one routing rule and one bridging rule
188 subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700189 Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP21), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700190 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
191 assertEquals(2, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700192 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700193 assertEquals(2, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700194 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_NATIVE)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700195
196 // Tagged host discovered on untagged port
197 // Expect: ignore the host. No rule is added.
198 subject = new DefaultHost(PROVIDER_ID, HOST_ID_TAGGED, HOST_MAC, HOST_VLAN_TAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700199 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700200 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
201 assertEquals(2, routingTable.size());
202 assertEquals(2, bridgingTable.size());
203
204 // Tagged host discovered on tagged port with the same IP
205 // Expect: update existing route, add one bridging rule
206 subject = new DefaultHost(PROVIDER_ID, HOST_ID_TAGGED, HOST_MAC, HOST_VLAN_TAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700207 Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP21), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700208 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
209 assertEquals(2, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700210 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
211 assertEquals(HOST_VLAN_TAGGED, routingTable.get(new RoutingTableKey(HOST_LOC13.deviceId(),
212 HOST_IP21.toIpPrefix())).vlanId);
Charles Chan2e2e3402017-06-19 14:00:53 -0700213 assertEquals(3, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700214 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, HOST_VLAN_TAGGED)));
215 }
216
217 @Test
218 public void testDualHomedHostAdded() throws Exception {
219 // Add a dual-homed host that has 2 locations
220 // Expect: add two routing rules and two bridging rules
221 Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
222 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
223 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
224 assertEquals(2, routingTable.size());
225 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
226 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
227 assertEquals(2, bridgingTable.size());
228 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
229 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700230 }
231
232 @Test
233 public void testHostRemoved() throws Exception {
234 Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700235 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700236
237 // Add a host
238 // Expect: add one routing rule and one bridging rule
239 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
240 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700241 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700242 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700243 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700244
245 // Remove the host
246 // Expect: add the routing rule and the bridging rule
Charles Chanf9a52702017-06-16 15:19:24 -0700247 hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, subject));
Charles Chan2e2e3402017-06-19 14:00:53 -0700248 assertEquals(0, routingTable.size());
Charles Chan2e2e3402017-06-19 14:00:53 -0700249 assertEquals(0, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700250 }
251
252 @Test
253 public void testDualHomedHostRemoved() throws Exception {
254 // Add a dual-homed host that has 2 locations
255 // Expect: add two routing rules and two bridging rules
256 Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
257 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
258 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
259 assertEquals(2, routingTable.size());
260 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
261 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
262 assertEquals(2, bridgingTable.size());
263 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
264 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
265
266 // Remove a dual-homed host that has 2 locations
267 // Expect: all routing and bridging rules are removed
268 hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, subject));
269 assertEquals(0, routingTable.size());
270 assertEquals(0, bridgingTable.size());
Charles Chan2e2e3402017-06-19 14:00:53 -0700271 }
272
273 @Test
274 public void testHostMoved() throws Exception {
275 Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700276 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700277 Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700278 Sets.newHashSet(HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700279 Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700280 Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700281
282 // Add a host
283 // Expect: add a new routing rule. no change to bridging rule.
284 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
285 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700286 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
287 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
288 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP13.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700289 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700290 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
291 assertNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700292
Charles Chanf9a52702017-06-16 15:19:24 -0700293 // Move the host to CP13, which has different subnet setting
Charles Chan2e2e3402017-06-19 14:00:53 -0700294 // Expect: remove routing rule. Change vlan in bridging rule.
Charles Chanf9a52702017-06-16 15:19:24 -0700295 hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host1));
Charles Chan2e2e3402017-06-19 14:00:53 -0700296 assertEquals(0, routingTable.size());
Charles Chan2e2e3402017-06-19 14:00:53 -0700297 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700298 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_NATIVE)));
299 assertNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700300
Charles Chanf9a52702017-06-16 15:19:24 -0700301 // Move the host to CP21, which has same subnet setting
Charles Chan2e2e3402017-06-19 14:00:53 -0700302 // Expect: add a new routing rule. Change vlan in bridging rule.
Charles Chanf9a52702017-06-16 15:19:24 -0700303 hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host3));
Charles Chan2e2e3402017-06-19 14:00:53 -0700304 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700305 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
306 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
307 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700308 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700309 assertNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
310 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
311 }
312
313 @Test
314 public void testDualHomedHostMoved() throws Exception {
315 Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
316 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
317 Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
318 Sets.newHashSet(HOST_LOC12, HOST_LOC22), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
319 Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
320 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP13, HOST_IP14), false);
321 Host host4 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
322 Sets.newHashSet(HOST_LOC11, HOST_LOC22), Sets.newHashSet(HOST_IP12, HOST_IP13), false);
323
324 // Add a host with IP11, IP12 and LOC11, LOC21
325 // Expect: 4 routing rules and 2 bridging rules
326 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
327 assertEquals(4, routingTable.size());
328 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
329 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
330 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
331 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
332 assertEquals(2, bridgingTable.size());
333 assertEquals(P1, bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
334 assertEquals(P1, bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
335
336 // Move the host to LOC12, LOC22 and keep the IP
337 // Expect: 4 routing rules and 2 bridging rules all at the new location
338 hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
339 assertEquals(4, routingTable.size());
340 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
341 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
342 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
343 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
344 assertEquals(2, bridgingTable.size());
345 assertEquals(P2, bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
346 assertEquals(P2, bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
347
348 // Move the host to LOC11, LOC21 and change the IP to IP13, IP14 at the same time
349 // Expect: 4 routing rules and 2 bridging rules all at the new location
350 hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
351 assertEquals(4, routingTable.size());
352 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP13.toIpPrefix())).portNumber);
353 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP14.toIpPrefix())).portNumber);
354 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV2, HOST_IP13.toIpPrefix())).portNumber);
355 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV2, HOST_IP14.toIpPrefix())).portNumber);
356 assertEquals(2, bridgingTable.size());
357 assertEquals(P1, bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
358 assertEquals(P1, bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
359
360 // Move the host to LOC11, LOC22 and change the IP to IP12, IP13 at the same time
361 // Expect: 4 routing rules and 2 bridging rules all at the new location
362 hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host4, host3));
363 assertEquals(4, routingTable.size());
364 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
365 assertEquals(P1, routingTable.get(new RoutingTableKey(DEV1, HOST_IP13.toIpPrefix())).portNumber);
366 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
367 assertEquals(P2, routingTable.get(new RoutingTableKey(DEV2, HOST_IP13.toIpPrefix())).portNumber);
368 assertEquals(2, bridgingTable.size());
369 assertEquals(P1, bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
370 assertEquals(P2, bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
Charles Chan2e2e3402017-06-19 14:00:53 -0700371 }
372
373 @Test
374 public void testHostUpdated() throws Exception {
375 Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700376 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700377 Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700378 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP21), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700379 Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
Charles Chanf9a52702017-06-16 15:19:24 -0700380 Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP12), false);
Charles Chan2e2e3402017-06-19 14:00:53 -0700381
382 // Add a host
Charles Chanf9a52702017-06-16 15:19:24 -0700383 // Expect: add one new routing rule. Add one new bridging rule.
Charles Chan2e2e3402017-06-19 14:00:53 -0700384 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
385 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700386 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
387 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
388 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700389 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700390 assertNotNull(bridgingTable.get(new BridingTableKey(HOST_LOC11.deviceId(), HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700391
392 // Update the host IP to same subnet
393 // Expect: update routing rule with new IP. No change to bridging rule.
394 hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host3, host1));
395 assertEquals(1, routingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700396 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
397 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
398 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
Charles Chan2e2e3402017-06-19 14:00:53 -0700399 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700400 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
Charles Chan2e2e3402017-06-19 14:00:53 -0700401
402 // Update the host IP to different subnet
403 // Expect: Remove routing rule. No change to bridging rule.
404 hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host3));
405 assertEquals(0, routingTable.size());
Charles Chan2e2e3402017-06-19 14:00:53 -0700406 assertEquals(1, bridgingTable.size());
Charles Chanf9a52702017-06-16 15:19:24 -0700407 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
408 }
409
410 @Test
411 public void testDualHomedHostUpdated() throws Exception {
412 Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
413 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
414 Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
415 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP21), false);
416 Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
417 Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP13, HOST_IP14), false);
418
419 // Add a dual-homed host with two locations and two IPs
420 // Expect: add four new routing rules. Add two new bridging rules
421 hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
422 assertEquals(4, routingTable.size());
423 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
424 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
425 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
426 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
427 assertEquals(2, bridgingTable.size());
428 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
429 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
430
431 // Update both host IPs
432 // Expect: update routing rules with new IP. No change to bridging rule.
433 hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host3, host1));
434 assertEquals(4, routingTable.size());
435 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
436 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
437 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP13.toIpPrefix())));
438 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP14.toIpPrefix())));
439 assertNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
440 assertNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
441 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP13.toIpPrefix())));
442 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP14.toIpPrefix())));
443 assertEquals(2, bridgingTable.size());
444 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
445 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
446
447 // Update one of the host IP to different subnet
448 // Expect: update routing rule with new IP. No change to bridging rule.
449 hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host3));
450 assertEquals(2, routingTable.size());
451 assertNotNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
452 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
453 assertNull(routingTable.get(new RoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
454 assertNotNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
455 assertNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP21.toIpPrefix())));
456 assertNull(routingTable.get(new RoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
457 assertEquals(2, bridgingTable.size());
458 assertNotNull(bridgingTable.get(new BridingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
459 assertNotNull(bridgingTable.get(new BridingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
460 }
461
462 @Test
463 public void testBridgingFwdObjBuilder() throws Exception {
464 assertNotNull(hostHandler.bridgingFwdObjBuilder(DEV2, HOST_MAC, HOST_VLAN_UNTAGGED, P1, false));
465 assertNull(hostHandler.bridgingFwdObjBuilder(DEV2, HOST_MAC, HOST_VLAN_UNTAGGED, P3, false));
Charles Chan2e2e3402017-06-19 14:00:53 -0700466 }
467
468 class MockSegmentRoutingManager extends SegmentRoutingManager {
469 MockSegmentRoutingManager() {
470 appId = new DefaultApplicationId(1, SegmentRoutingManager.APP_NAME);
471 }
472
473 @Override
474 public int getPortNextObjectiveId(DeviceId deviceId, PortNumber portNum,
475 TrafficTreatment treatment,
476 TrafficSelector meta,
477 boolean createIfMissing) {
478 int nextId = atomicNextId.incrementAndGet();
479 nextTable.put(nextId, treatment);
480 return nextId;
481 }
482 }
483
484 class MockInterfaceService extends InterfaceServiceAdapter {
485 @Override
486 public Set<Interface> getInterfacesByPort(ConnectPoint cp) {
487 Interface intf = null;
488
Charles Chanf9a52702017-06-16 15:19:24 -0700489 if (CP11.equals(cp)) {
490 intf = new Interface(null, CP11, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
Charles Chan2e2e3402017-06-19 14:00:53 -0700491 INTF_VLAN_UNTAGGED, null, null);
Charles Chanf9a52702017-06-16 15:19:24 -0700492 } else if (CP12.equals(cp)) {
493 intf = new Interface(null, CP12, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
494 INTF_VLAN_UNTAGGED, null, null);
495 } else if (CP13.equals(cp)) {
496 intf = new Interface(null, CP13, Lists.newArrayList(INTF_IP2), MacAddress.NONE, null,
Charles Chan2e2e3402017-06-19 14:00:53 -0700497 null, INTF_VLAN_TAGGED, INTF_VLAN_NATIVE);
Charles Chanf9a52702017-06-16 15:19:24 -0700498 } else if (CP21.equals(cp)) {
499 intf = new Interface(null, CP21, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
500 INTF_VLAN_UNTAGGED, null, null);
501 } else if (CP22.equals(cp)) {
502 intf = new Interface(null, CP22, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
Charles Chan2e2e3402017-06-19 14:00:53 -0700503 INTF_VLAN_UNTAGGED, null, null);
504 }
505
506 return Objects.nonNull(intf) ? Sets.newHashSet(intf) : Sets.newHashSet();
507 }
508 }
509
510 class MockFlowObjectiveService extends FlowObjectiveServiceAdapter {
511 @Override
512 public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
513 TrafficSelector selector = forwardingObjective.selector();
514 TrafficTreatment treatment = nextTable.get(forwardingObjective.nextId());
515 MacAddress macAddress = ((EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST)).mac();
516 VlanId vlanId = ((VlanIdCriterion) selector.getCriterion(Criterion.Type.VLAN_VID)).vlanId();
517
518 boolean popVlan = treatment.allInstructions().stream()
519 .filter(instruction -> instruction.type().equals(Instruction.Type.L2MODIFICATION))
520 .anyMatch(instruction -> ((L2ModificationInstruction) instruction).subtype()
521 .equals(L2ModificationInstruction.L2SubType.VLAN_POP));
522 PortNumber portNumber = treatment.allInstructions().stream()
523 .filter(instruction -> instruction.type().equals(Instruction.Type.OUTPUT))
524 .map(instruction -> ((Instructions.OutputInstruction) instruction).port()).findFirst().orElse(null);
525 if (portNumber == null) {
526 throw new IllegalArgumentException();
527 }
528
529 Objective.Operation op = forwardingObjective.op();
530
531 BridingTableKey btKey = new BridingTableKey(deviceId, macAddress, vlanId);
532 BridingTableValue btValue = new BridingTableValue(popVlan, portNumber);
533
534 if (op.equals(Objective.Operation.ADD)) {
535 bridgingTable.put(btKey, btValue);
536 } else if (op.equals(Objective.Operation.REMOVE)) {
537 bridgingTable.remove(btKey, btValue);
538 } else {
539 throw new IllegalArgumentException();
540 }
541 }
542 }
543
Charles Chanf9a52702017-06-16 15:19:24 -0700544 class MockHostService extends HostServiceAdapter {
545 @Override
546 public Set<Host> getHosts() {
547 return Sets.newHashSet(HOST1);
548 }
549 }
550
Charles Chan2e2e3402017-06-19 14:00:53 -0700551 class MockRoutingRulePopulator extends RoutingRulePopulator {
552 MockRoutingRulePopulator() {
553 super(srManager);
554 }
555
556 @Override
557 public void populateRoute(DeviceId deviceId, IpPrefix prefix,
558 MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
559 RoutingTableKey rtKey = new RoutingTableKey(deviceId, prefix);
560 RoutingTableValue rtValue = new RoutingTableValue(outPort, hostMac, hostVlanId);
561 routingTable.put(rtKey, rtValue);
562 }
563
564 @Override
565 public void revokeRoute(DeviceId deviceId, IpPrefix prefix,
566 MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
567 RoutingTableKey rtKey = new RoutingTableKey(deviceId, prefix);
568 RoutingTableValue rtValue = new RoutingTableValue(outPort, hostMac, hostVlanId);
569 routingTable.remove(rtKey, rtValue);
570 }
571 }
572
573 class BridingTableKey {
574 DeviceId deviceId;
575 MacAddress macAddress;
576 VlanId vlanId;
577
578 BridingTableKey(DeviceId deviceId, MacAddress macAddress, VlanId vlanId) {
579 this.deviceId = deviceId;
580 this.macAddress = macAddress;
581 this.vlanId = vlanId;
582 }
583
584 @Override
585 public boolean equals(final Object obj) {
586 if (this == obj) {
587 return true;
588 }
589 if (!(obj instanceof BridingTableKey)) {
590 return false;
591 }
592 final BridingTableKey other = (BridingTableKey) obj;
593 return Objects.equals(this.macAddress, other.macAddress) &&
594 Objects.equals(this.deviceId, other.deviceId) &&
595 Objects.equals(this.vlanId, other.vlanId);
596 }
597
598 @Override
599 public int hashCode() {
600 return Objects.hash(macAddress, vlanId);
601 }
602 }
603
604 class BridingTableValue {
605 boolean popVlan;
606 PortNumber portNumber;
607
608 BridingTableValue(boolean popVlan, PortNumber portNumber) {
609 this.popVlan = popVlan;
610 this.portNumber = portNumber;
611 }
612
613 @Override
614 public boolean equals(final Object obj) {
615 if (this == obj) {
616 return true;
617 }
618 if (!(obj instanceof BridingTableValue)) {
619 return false;
620 }
621 final BridingTableValue other = (BridingTableValue) obj;
622 return Objects.equals(this.popVlan, other.popVlan) &&
623 Objects.equals(this.portNumber, other.portNumber);
624 }
625
626 @Override
627 public int hashCode() {
628 return Objects.hash(popVlan, portNumber);
629 }
630 }
631
632 class RoutingTableKey {
633 DeviceId deviceId;
634 IpPrefix ipPrefix;
635
636 RoutingTableKey(DeviceId deviceId, IpPrefix ipPrefix) {
637 this.deviceId = deviceId;
638 this.ipPrefix = ipPrefix;
639 }
640
641 @Override
642 public boolean equals(final Object obj) {
643 if (this == obj) {
644 return true;
645 }
646 if (!(obj instanceof RoutingTableKey)) {
647 return false;
648 }
649 final RoutingTableKey other = (RoutingTableKey) obj;
650 return Objects.equals(this.deviceId, other.deviceId) &&
651 Objects.equals(this.ipPrefix, other.ipPrefix);
652 }
653
654 @Override
655 public int hashCode() {
656 return Objects.hash(deviceId, ipPrefix);
657 }
658 }
659
660 class RoutingTableValue {
661 PortNumber portNumber;
662 MacAddress macAddress;
663 VlanId vlanId;
664
665 RoutingTableValue(PortNumber portNumber, MacAddress macAddress, VlanId vlanId) {
666 this.portNumber = portNumber;
667 this.macAddress = macAddress;
668 this.vlanId = vlanId;
669 }
670
671 @Override
672 public boolean equals(final Object obj) {
673 if (this == obj) {
674 return true;
675 }
676 if (!(obj instanceof RoutingTableValue)) {
677 return false;
678 }
679 final RoutingTableValue other = (RoutingTableValue) obj;
680 return Objects.equals(this.portNumber, other.portNumber) &&
681 Objects.equals(this.macAddress, other.macAddress) &&
682 Objects.equals(this.vlanId, other.vlanId);
683 }
684
685 @Override
686 public int hashCode() {
687 return Objects.hash(portNumber, macAddress, vlanId);
688 }
689 }
690
691}