blob: d5227c3b7fa4b522d30ed111b9eeabcad5a76723 [file] [log] [blame]
Daniel Park4d486842018-07-24 17:06:43 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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 */
16package org.onosproject.openstacknetworking.impl;
17
18import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Maps;
20import com.google.common.util.concurrent.MoreExecutors;
21import org.junit.After;
22import org.junit.Before;
23import org.junit.Test;
24import org.onlab.junit.TestUtils;
25import org.onlab.packet.DeserializationException;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.ICMP;
Daniel Parkf740c2e2018-07-31 10:49:05 +090028import org.onlab.packet.ICMPEcho;
Daniel Park4d486842018-07-24 17:06:43 +090029import org.onlab.packet.IPv4;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.packet.VlanId;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreServiceAdapter;
35import org.onosproject.core.DefaultApplicationId;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.PortNumber;
38import org.onosproject.net.packet.DefaultInboundPacket;
39import org.onosproject.net.packet.DefaultPacketContext;
40import org.onosproject.net.packet.InboundPacket;
41import org.onosproject.net.packet.OutboundPacket;
42import org.onosproject.net.packet.PacketContext;
43import org.onosproject.net.packet.PacketProcessor;
44import org.onosproject.net.packet.PacketServiceAdapter;
45import org.onosproject.openstacknetworking.api.Constants;
46import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
47import org.onosproject.openstacknetworking.api.InstancePort;
48import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
49import org.onosproject.openstacknode.api.OpenstackNode;
50import org.onosproject.openstacknode.api.OpenstackNodeAdapter;
51import org.onosproject.store.service.TestStorageService;
52import org.openstack4j.model.network.ExternalGateway;
53import org.openstack4j.model.network.Port;
54import org.openstack4j.model.network.Router;
55import org.openstack4j.model.network.RouterInterface;
56import org.openstack4j.model.network.Subnet;
57import org.openstack4j.openstack.networking.domain.NeutronPort;
58import org.openstack4j.openstack.networking.domain.NeutronRouter;
59import org.openstack4j.openstack.networking.domain.NeutronSubnet;
60
61import java.io.InputStream;
62import java.nio.ByteBuffer;
63import java.util.Map;
64import java.util.Objects;
65import java.util.Set;
66import java.util.stream.Collectors;
67
68import static org.junit.Assert.assertEquals;
69import static org.junit.Assert.fail;
70import static org.onlab.packet.Ethernet.TYPE_IPV4;
71import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
72import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
73import static org.onosproject.net.NetTestTools.connectPoint;
74
75public class OpenstackRoutingIcmpHandlerTest {
76 private OpenstackRoutingIcmpHandler icmpHandler;
77 private static final byte CODE_ECHO_REQUEST = 0x00;
78 private static final byte CODE_ECHO_REPLY = 0x00;
79
80 protected PacketProcessor packetProcessor;
81 private InstancePort instancePort1;
82 private InstancePort instancePort2;
83 private InstancePort instancePort3;
84 private RouterInterface routerInterface1;
85 private Router router1;
86
87 private MacAddress srcMacPort1 = MacAddress.valueOf("11:22:33:44:55:66");
88 private IpAddress srcIpPort1 = IpAddress.valueOf("10.0.0.3");
89 private DeviceId srcDeviceId1 = DeviceId.deviceId("of:000000000000000a");
90 private PortNumber srcPortNum1 = PortNumber.portNumber(1);
91 private IpAddress targetIpToGw = IpAddress.valueOf("10.0.0.1");
92 private IpAddress targetIpToExternal = IpAddress.valueOf("8.8.8.8");
93 private IpAddress sNatIp = IpAddress.valueOf("172.27.0.13");
94 private MacAddress targetMac = Constants.DEFAULT_GATEWAY_MAC;
95 private MacAddress peerRouterMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
96 private Port port1;
97 private Port externalPort;
98 private Subnet subnet1;
99
100 Map<String, Port> portMap = Maps.newHashMap();
101 Map<String, InstancePort> instancePortMap = Maps.newHashMap();
102 Map<String, Router> osRouterMap = Maps.newHashMap();
103 Map<String, RouterInterface> osRouterInterfaceMap = Maps.newHashMap();
104
105 /**
106 * Initial setup for this unit test.
107 */
108 @Before
109 public void setUp() {
110
111 icmpHandler = new OpenstackRoutingIcmpHandler();
112
113 icmpHandler.coreService = new TestCoreService();
114 icmpHandler.packetService = new TestPacketService();
115 icmpHandler.storageService = new TestStorageService();
116 icmpHandler.osNodeService = new TestOpenstackNodeService();
117 icmpHandler.instancePortService = new TestInstancePortService();
118 icmpHandler.osNetworkService = new TestOpenstackNetworkService();
119 icmpHandler.osRouterService = new TestOpenstackRouterService();
120 TestUtils.setField(icmpHandler, "eventExecutor", MoreExecutors.newDirectExecutorService());
121 icmpHandler.activate();
122
123 createPort();
124 createSubnet();
125 createInstancePortMap();
126 createRouterInterfaceMap();
127 createRouterMap();
128 }
129
130 /**
131 * Tears down all of this unit test.
132 */
133 @After
134 public void tearDown() {
135 icmpHandler.deactivate();
136
137 }
138
139 /**
140 * Tests the icmp request to gateway.
141 */
142 @Test
143 public void testRequestToGw() {
144 Ethernet icmpRequest = constructIcmpRequestPacket(srcIpPort1,
145 srcMacPort1,
146 targetIpToGw,
147 targetMac,
148 TYPE_ECHO_REQUEST);
149 sendPacket(icmpRequest);
150 }
151
152 /**
153 * Tests the icmp request to external.
154 */
155 @Test
156 public void testRequestToExternal() {
157 Ethernet icmpRequest = constructIcmpRequestPacket(srcIpPort1,
158 srcMacPort1,
159 targetIpToExternal,
160 targetMac,
161 TYPE_ECHO_REQUEST);
162
163 sendPacket(icmpRequest);
164
165 Ethernet icmpResponse = constructIcmpRequestPacket(targetIpToExternal,
166 peerRouterMac,
167 sNatIp,
168 targetMac,
169 TYPE_ECHO_REPLY);
170
171 sendPacket(icmpResponse);
172 }
173
174 private void sendPacket(Ethernet ethernet) {
175 final ByteBuffer byteBuffer = ByteBuffer.wrap(ethernet.serialize());
176 InboundPacket inPacket = new DefaultInboundPacket(connectPoint(srcDeviceId1.toString(),
177 Integer.parseInt(srcPortNum1.toString())),
178 ethernet,
179 byteBuffer);
180
181 PacketContext context = new TestPacketContext(127L, inPacket, null, false);
182 packetProcessor.process(context);
183 }
184
185 private void validatePacket(Ethernet ethernet) {
186 IPv4 ipPacket = (IPv4) ethernet.getPayload();
187
188 if (IPv4.fromIPv4Address(ipPacket.getSourceAddress()).equals(targetIpToGw.toString())) {
189 validateIcmpReqToGw(ipPacket);
190 } else if (IPv4.fromIPv4Address(ipPacket.getSourceAddress())
191 .equals(sNatIp.toString())) {
192 validateIcmpReqToExternal(ipPacket);
193 } else if (IPv4.fromIPv4Address(ipPacket.getSourceAddress())
194 .equals(targetIpToExternal.toString())) {
195 validateIcmpRespFromExternal(ipPacket);
196 }
197 }
198
199 private void validateIcmpRespFromExternal(IPv4 ipPacket) {
200 ICMP icmpResp = (ICMP) ipPacket.getPayload();
Daniel Parkf740c2e2018-07-31 10:49:05 +0900201 ICMPEcho icmpEchoResp = (ICMPEcho) icmpResp.getPayload();
202 short icmpId = icmpEchoResp.getIdentifier();
203 short seqNum = icmpEchoResp.getSequenceNum();
Daniel Park4d486842018-07-24 17:06:43 +0900204
205 assertEquals(icmpResp.getIcmpType(), TYPE_ECHO_REPLY);
206 assertEquals(icmpResp.getIcmpCode(), CODE_ECHO_REPLY);
207 assertEquals(icmpId, 0);
208 assertEquals(seqNum, 0);
209 assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), targetIpToExternal.toString());
210 assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), srcIpPort1.toString());
211 }
212
213 private void validateIcmpReqToExternal(IPv4 ipPacket) {
214 ICMP icmpReq = (ICMP) ipPacket.getPayload();
Daniel Parkf740c2e2018-07-31 10:49:05 +0900215 ICMPEcho icmpEchoReq = (ICMPEcho) icmpReq.getPayload();
216 short icmpId = icmpEchoReq.getIdentifier();
217 short seqNum = icmpEchoReq.getSequenceNum();
Daniel Park4d486842018-07-24 17:06:43 +0900218
219 assertEquals(icmpReq.getIcmpType(), TYPE_ECHO_REQUEST);
220 assertEquals(icmpReq.getIcmpCode(), CODE_ECHO_REQUEST);
221 assertEquals(icmpId, 0);
222 assertEquals(seqNum, 0);
223 assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), sNatIp.toString());
224 assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), targetIpToExternal.toString());
225
226 }
227 private void validateIcmpReqToGw(IPv4 ipPacket) {
228 ICMP icmpReq = (ICMP) ipPacket.getPayload();
Daniel Parkf740c2e2018-07-31 10:49:05 +0900229 ICMPEcho icmpEchoReq = (ICMPEcho) icmpReq.getPayload();
230 short icmpId = icmpEchoReq.getIdentifier();
231 short seqNum = icmpEchoReq.getSequenceNum();
Daniel Park4d486842018-07-24 17:06:43 +0900232
233 assertEquals(icmpReq.getIcmpType(), TYPE_ECHO_REPLY);
234 assertEquals(icmpReq.getIcmpCode(), CODE_ECHO_REPLY);
235 assertEquals(icmpId, 0);
236 assertEquals(seqNum, 0);
237 assertEquals(IPv4.fromIPv4Address(ipPacket.getSourceAddress()), targetIpToGw.toString());
238 assertEquals(IPv4.fromIPv4Address(ipPacket.getDestinationAddress()), srcIpPort1.toString());
239 }
240
241 private Ethernet constructIcmpRequestPacket(IpAddress srcIp,
242 MacAddress srcMac,
243 IpAddress dstIp,
244 MacAddress dstMac, byte icmpType) {
245 try {
Daniel Parkf740c2e2018-07-31 10:49:05 +0900246 ICMPEcho icmpEcho = new ICMPEcho();
247 icmpEcho.setIdentifier((short) 0)
Daniel Park4d486842018-07-24 17:06:43 +0900248 .setSequenceNum((short) 0);
Daniel Parkf740c2e2018-07-31 10:49:05 +0900249 ByteBuffer byteBufferIcmpEcho = ByteBuffer.wrap(icmpEcho.serialize());
Daniel Park4d486842018-07-24 17:06:43 +0900250
Daniel Parkf740c2e2018-07-31 10:49:05 +0900251 ICMP icmp = new ICMP();
252 icmp.setIcmpType(icmpType)
253 .setIcmpCode(icmpType == TYPE_ECHO_REQUEST ? CODE_ECHO_REQUEST : CODE_ECHO_REPLY)
254 .setChecksum((short) 0);
255
256 icmp.setPayload(ICMPEcho.deserializer().deserialize(byteBufferIcmpEcho.array(),
257 0, ICMPEcho.ICMP_ECHO_HEADER_LENGTH));
258
259 ByteBuffer byteBufferIcmp = ByteBuffer.wrap(icmp.serialize());
Daniel Park4d486842018-07-24 17:06:43 +0900260
261 IPv4 iPacket = new IPv4();
262 iPacket.setDestinationAddress(dstIp.toString());
263 iPacket.setSourceAddress(srcIp.toString());
264 iPacket.setTtl((byte) 64);
265 iPacket.setChecksum((short) 0);
266 iPacket.setDiffServ((byte) 0);
267 iPacket.setProtocol(IPv4.PROTOCOL_ICMP);
268
Daniel Parkf740c2e2018-07-31 10:49:05 +0900269 iPacket.setPayload(ICMP.deserializer().deserialize(byteBufferIcmp.array(), 0, 8));
Daniel Park4d486842018-07-24 17:06:43 +0900270
271 Ethernet ethPacket = new Ethernet();
272
273 ethPacket.setEtherType(TYPE_IPV4);
274 ethPacket.setSourceMACAddress(srcMac);
275 ethPacket.setDestinationMACAddress(dstMac);
276 ethPacket.setPayload(iPacket);
277
278 return ethPacket;
279 } catch (DeserializationException e) {
280 return null;
281 }
282 }
283
284 private class TestCoreService extends CoreServiceAdapter {
285 @Override
286 public ApplicationId registerApplication(String name) {
287 return new DefaultApplicationId(200, "test");
288 }
289 }
290
291 /**
292 * Mocks the PacketService.
293 */
294 private class TestPacketService extends PacketServiceAdapter {
295 @Override
296 public void addProcessor(PacketProcessor processor, int priority) {
297 packetProcessor = processor;
298 }
299
300 @Override
301 public void emit(OutboundPacket packet) {
302 try {
303 Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
304 0, packet.data().array().length);
305 validatePacket(eth);
306 } catch (Exception e) {
307 fail(e.getMessage());
308 }
309 }
310 }
311
312 /**
313 * Mocks the OpenstackNodeService.
314 */
315 private class TestOpenstackNodeService extends OpenstackNodeServiceAdapter {
316
317 @Override
318 public OpenstackNode node(DeviceId deviceId) {
319 return new TestOpenstackNode();
320 }
321 }
322
323 /**
324 * Mocks the InstancePortService.
325 */
326 private class TestInstancePortService extends InstancePortServiceAdapter {
327 @Override
328 public InstancePort instancePort(MacAddress macAddress) {
329 return instancePortMap.values().stream()
330 .filter(port -> Objects.equals(port.macAddress(), macAddress))
331 .findAny().orElse(null);
332 }
333
334 @Override
335 public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
336 return instancePortMap.values().stream()
337 .filter(port -> port.networkId().equals(osNetId))
338 .filter(port -> port.ipAddress().equals(ipAddress))
339 .findFirst().orElse(null);
340 }
341
342 @Override
343 public InstancePort instancePort(String osPortId) {
344 return instancePortMap.get(osPortId);
345 }
346
347 @Override
348 public Set<InstancePort> instancePorts() {
349 return ImmutableSet.copyOf(instancePortMap.values());
350 }
351
352 @Override
353 public Set<InstancePort> instancePorts(String osNetId) {
354 Set<InstancePort> ports = instancePortMap.values().stream()
355 .filter(port -> port.networkId().equals(osNetId))
356 .collect(Collectors.toSet());
357
358 return ImmutableSet.copyOf(ports);
359 }
360 }
361
362 /**
363 * Mocks the OpenstackNetworkService.
364 */
365 private class TestOpenstackNetworkService extends OpenstackNetworkServiceAdapter {
366 @Override
367 public Set<Port> ports(String netId) {
368 return ImmutableSet.copyOf(portMap.values());
369 }
370 @Override
371 public Port port(String portId) {
372
373 return port1;
374 }
375
376 @Override
377 public Subnet subnet(String subnetId) {
378
379 return subnet1;
380 }
381
382 @Override
383 public ExternalPeerRouter externalPeerRouter(ExternalGateway externalGateway) {
384 return DefaultExternalPeerRouter.builder()
385 .ipAddress(IpAddress.valueOf("172.27.0.1"))
386 .macAddress(peerRouterMac)
387 .vlanId(VlanId.NONE)
388 .build();
389 }
390 }
391
392 /**
393 * Mocks the OpenstackRouterService.
394 */
395 private class TestOpenstackRouterService extends OpenstackRouterServiceAdapter {
396 @Override
397 public Set<RouterInterface> routerInterfaces() {
398 return ImmutableSet.copyOf(osRouterInterfaceMap.values());
399 }
400
401 @Override
402 public Router router(String osRouterId) {
403 return osRouterMap.get(osRouterId);
404 }
405 public Set<RouterInterface> routerInterfaces(String osRouterId) {
406 return osRouterInterfaceMap.values().stream()
407 .filter(iface -> iface.getId().equals(osRouterId))
408 .collect(Collectors.toSet());
409 }
410
411 }
412
413 /**
414 * Mocks the DefaultPacket context.
415 */
416 private final class TestPacketContext extends DefaultPacketContext {
417 private TestPacketContext(long time, InboundPacket inPkt,
418 OutboundPacket outPkt, boolean block) {
419 super(time, inPkt, outPkt, block);
420 }
421
422 @Override
423 public void send() {
424 // We don't send anything out.
425 }
426 }
427
428
429 private void createPort() {
430 InputStream portJsonStream1 = OpenstackRoutingIcmpHandlerTest.class
431 .getResourceAsStream("openstack-port-1.json");
432 port1 = (Port) OpenstackNetworkingUtil.jsonToModelEntity(portJsonStream1, NeutronPort.class);
433
434 InputStream portJsonStream2 = OpenstackRoutingIcmpHandlerTest.class
435 .getResourceAsStream("openstack-port-external.json");
436 externalPort = (Port) OpenstackNetworkingUtil.jsonToModelEntity(portJsonStream2, NeutronPort.class);
437
438 portMap.put(port1.getId(), port1);
439 portMap.put(externalPort.getId(), externalPort);
440
441 }
442
443 private void createSubnet() {
444 InputStream subnetJsonStream1 = OpenstackRoutingIcmpHandlerTest.class
445 .getResourceAsStream("openstack-subnet-1.json");
446 subnet1 = (Subnet) OpenstackNetworkingUtil.jsonToModelEntity(subnetJsonStream1, NeutronSubnet.class);
447 }
448
449 private void createRouterInterfaceMap() {
450 routerInterface1 = new TestRouterInterface("router-id-1",
451 "subnet-id-1",
452 "router-interface-id-1",
453 "tenant-id-1");
454
455 osRouterInterfaceMap.put(routerInterface1.getPortId(), routerInterface1);
456 }
457
458 private void createRouterMap() {
459 InputStream routerStream1 = OpenstackRoutingIcmpHandlerTest.class
460 .getResourceAsStream("openstack-router-1.json");
461 router1 = (Router)
462 OpenstackNetworkingUtil.jsonToModelEntity(routerStream1, NeutronRouter.class);
463 osRouterMap.put(router1.getId(), router1);
464
465 }
466 private void createInstancePortMap() {
467 instancePort1 = DefaultInstancePort.builder()
468 .networkId("net-id-1")
469 .portId("ce705c24-c1ef-408a-bda3-7bbd946164ab")
470 .deviceId(srcDeviceId1)
471 .portNumber(srcPortNum1)
472 .ipAddress(srcIpPort1)
473 .macAddress(srcMacPort1)
474 .state(InstancePort.State.valueOf("ACTIVE"))
475 .build();
476
477 instancePort2 = DefaultInstancePort.builder()
478 .networkId("net-id-2")
479 .portId("port-id-2")
480 .deviceId(DeviceId.deviceId("of:000000000000000b"))
481 .portNumber(PortNumber.portNumber(2))
482 .ipAddress(IpAddress.valueOf("10.10.10.2"))
483 .macAddress(MacAddress.valueOf("22:33:44:55:66:11"))
484 .state(InstancePort.State.valueOf("ACTIVE"))
485 .build();
486
487 instancePort3 = DefaultInstancePort.builder()
488 .networkId("net-id-3")
489 .portId("port-id-3")
490 .deviceId(DeviceId.deviceId("of:000000000000000c"))
491 .oldDeviceId(DeviceId.deviceId("of:000000000000000d"))
492 .oldPortNumber(PortNumber.portNumber(4, "tap-4"))
493 .portNumber(PortNumber.portNumber(3, "tap-3"))
494 .ipAddress(IpAddress.valueOf("10.10.10.3"))
495 .macAddress(MacAddress.valueOf("33:44:55:66:11:22"))
496 .state(InstancePort.State.valueOf("ACTIVE"))
497 .build();
498
499 instancePortMap.put(instancePort1.portId(), instancePort1);
500 instancePortMap.put(instancePort2.portId(), instancePort2);
501 instancePortMap.put(instancePort3.portId(), instancePort3);
502 }
503
504
505 private class TestOpenstackNode extends OpenstackNodeAdapter {
506 public TestOpenstackNode() {
507 super();
508 }
509 @Override
510 public PortNumber uplinkPortNum() {
511 return PortNumber.portNumber(1);
512 }
513 }
514}