blob: fa68761221a202549d169d78b798f3f074c2b236 [file] [log] [blame]
Jonathan Hart704ca142014-10-09 09:34:39 -07001package org.onlab.onos.net.proxyarp.impl;
2
3import static org.easymock.EasyMock.anyObject;
4import static org.easymock.EasyMock.createMock;
5import static org.easymock.EasyMock.expect;
6import static org.easymock.EasyMock.replay;
7import static org.junit.Assert.assertEquals;
8import static org.junit.Assert.assertFalse;
9import static org.junit.Assert.assertTrue;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.Comparator;
15import java.util.List;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070016import java.util.Set;
Jonathan Hart704ca142014-10-09 09:34:39 -070017
18import org.junit.Before;
19import org.junit.Test;
20import org.onlab.onos.net.ConnectPoint;
21import org.onlab.onos.net.DefaultHost;
22import org.onlab.onos.net.Device;
23import org.onlab.onos.net.DeviceId;
24import org.onlab.onos.net.Host;
25import org.onlab.onos.net.HostId;
26import org.onlab.onos.net.HostLocation;
27import org.onlab.onos.net.Link;
28import org.onlab.onos.net.Port;
29import org.onlab.onos.net.PortNumber;
30import org.onlab.onos.net.device.DeviceListener;
31import org.onlab.onos.net.device.DeviceService;
32import org.onlab.onos.net.flow.instructions.Instruction;
33import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
34import org.onlab.onos.net.host.HostService;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070035import org.onlab.onos.net.host.PortAddresses;
Jonathan Hart704ca142014-10-09 09:34:39 -070036import org.onlab.onos.net.link.LinkListener;
37import org.onlab.onos.net.link.LinkService;
38import org.onlab.onos.net.packet.OutboundPacket;
39import org.onlab.onos.net.packet.PacketProcessor;
40import org.onlab.onos.net.packet.PacketService;
41import org.onlab.onos.net.provider.ProviderId;
42import org.onlab.packet.ARP;
43import org.onlab.packet.Ethernet;
44import org.onlab.packet.IpPrefix;
45import org.onlab.packet.MacAddress;
46import org.onlab.packet.VlanId;
47
48import com.google.common.collect.Sets;
49
50/**
51 * Tests for the {@link ProxyArpManager} class.
52 */
53public class ProxyArpManagerTest {
54
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070055 private static final int NUM_DEVICES = 6;
Jonathan Hart704ca142014-10-09 09:34:39 -070056 private static final int NUM_PORTS_PER_DEVICE = 3;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070057 private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
58 private static final int NUM_FLOOD_PORTS = 3;
Jonathan Hart704ca142014-10-09 09:34:39 -070059
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -070060 private static final IpPrefix IP1 = IpPrefix.valueOf("192.168.1.1/24");
61 private static final IpPrefix IP2 = IpPrefix.valueOf("192.168.1.2/24");
Jonathan Hart704ca142014-10-09 09:34:39 -070062
63 private static final ProviderId PID = new ProviderId("of", "foo");
64
65 private static final VlanId VLAN1 = VlanId.vlanId((short) 1);
66 private static final VlanId VLAN2 = VlanId.vlanId((short) 2);
67 private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01");
68 private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
69 private static final HostId HID1 = HostId.hostId(MAC1, VLAN1);
70 private static final HostId HID2 = HostId.hostId(MAC2, VLAN1);
71
72 private static final DeviceId DID1 = getDeviceId(1);
73 private static final DeviceId DID2 = getDeviceId(2);
74 private static final PortNumber P1 = PortNumber.portNumber(1);
75 private static final HostLocation LOC1 = new HostLocation(DID1, P1, 123L);
76 private static final HostLocation LOC2 = new HostLocation(DID2, P1, 123L);
77
78 private ProxyArpManager proxyArp;
79
80 private TestPacketService packetService;
81
82 private DeviceService deviceService;
83 private LinkService linkService;
84 private HostService hostService;
85
86 @Before
87 public void setUp() throws Exception {
88 proxyArp = new ProxyArpManager();
89 packetService = new TestPacketService();
90 proxyArp.packetService = packetService;
91
92 // Create a host service mock here. Must be replayed by tests once the
93 // expectations have been set up
94 hostService = createMock(HostService.class);
95 proxyArp.hostService = hostService;
96
97 createTopology();
98 proxyArp.deviceService = deviceService;
99 proxyArp.linkService = linkService;
100
101 proxyArp.activate();
102 }
103
104 /**
105 * Creates a fake topology to feed into the ARP module.
106 * <p/>
107 * The default topology is a unidirectional ring topology. Each switch has
108 * 3 ports. Ports 2 and 3 have the links to neighbor switches, and port 1
109 * is free (edge port).
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700110 * The first half of the switches have IP addresses configured on their
111 * free ports (port 1). The second half of the switches have no IP
112 * addresses configured.
Jonathan Hart704ca142014-10-09 09:34:39 -0700113 */
114 private void createTopology() {
115 deviceService = createMock(DeviceService.class);
116 linkService = createMock(LinkService.class);
117
118 deviceService.addListener(anyObject(DeviceListener.class));
119 linkService.addListener(anyObject(LinkListener.class));
120
121 createDevices(NUM_DEVICES, NUM_PORTS_PER_DEVICE);
122 createLinks(NUM_DEVICES);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700123 addAddressBindings();
Jonathan Hart704ca142014-10-09 09:34:39 -0700124 }
125
126 /**
127 * Creates the devices for the fake topology.
128 */
129 private void createDevices(int numDevices, int numPorts) {
130 List<Device> devices = new ArrayList<>();
131
132 for (int i = 1; i <= numDevices; i++) {
133 DeviceId devId = getDeviceId(i);
134 Device device = createMock(Device.class);
135 expect(device.id()).andReturn(devId).anyTimes();
136 replay(device);
137
138 devices.add(device);
139
140 List<Port> ports = new ArrayList<>();
141 for (int j = 1; j <= numPorts; j++) {
142 Port port = createMock(Port.class);
143 expect(port.number()).andReturn(PortNumber.portNumber(j)).anyTimes();
144 replay(port);
145 ports.add(port);
146 }
147
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700148 expect(deviceService.getPorts(devId)).andReturn(ports).anyTimes();
149 expect(deviceService.getDevice(devId)).andReturn(device).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700150 }
151
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700152 expect(deviceService.getDevices()).andReturn(devices).anyTimes();
Jonathan Hart704ca142014-10-09 09:34:39 -0700153 replay(deviceService);
154 }
155
156 /**
157 * Creates the links for the fake topology.
158 * NB: Only unidirectional links are created, as for this purpose all we
159 * need is to occupy the ports with some link.
160 */
161 private void createLinks(int numDevices) {
162 List<Link> links = new ArrayList<Link>();
163
164 for (int i = 1; i <= numDevices; i++) {
165 ConnectPoint src = new ConnectPoint(
166 getDeviceId(i),
167 PortNumber.portNumber(2));
168 ConnectPoint dst = new ConnectPoint(
169 getDeviceId((i + 1 > numDevices) ? 1 : i + 1),
170 PortNumber.portNumber(3));
171
172 Link link = createMock(Link.class);
173 expect(link.src()).andReturn(src).anyTimes();
174 expect(link.dst()).andReturn(dst).anyTimes();
175 replay(link);
176
177 links.add(link);
178 }
179
180 expect(linkService.getLinks()).andReturn(links).anyTimes();
181 replay(linkService);
182 }
183
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700184 private void addAddressBindings() {
185 Set<PortAddresses> addresses = Sets.newHashSet();
186
187 for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
188 ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
189 IpPrefix prefix1 = IpPrefix.valueOf("10.0." + (2 * i - 1) + ".1/24");
190 IpPrefix prefix2 = IpPrefix.valueOf("10.0." + (2 * i) + ".1/24");
191 PortAddresses pa = new PortAddresses(cp,
192 Sets.newHashSet(prefix1, prefix2), MacAddress.valueOf(i));
193 addresses.add(pa);
194
195 expect(hostService.getAddressBindingsForPort(cp))
196 .andReturn(pa).anyTimes();
197 }
198
199 expect(hostService.getAddressBindings()).andReturn(addresses).anyTimes();
200
201 for (int i = 1; i <= NUM_FLOOD_PORTS; i++) {
202 ConnectPoint cp = new ConnectPoint(getDeviceId(i + NUM_ADDRESS_PORTS),
203 P1);
204 expect(hostService.getAddressBindingsForPort(cp))
205 .andReturn(new PortAddresses(cp, null, null)).anyTimes();
206 }
207 }
208
Jonathan Hart704ca142014-10-09 09:34:39 -0700209 /**
210 * Tests {@link ProxyArpManager#known(IpPrefix)} in the case where the
211 * IP address is not known.
212 * Verifies the method returns false.
213 */
214 @Test
215 public void testNotKnown() {
216 expect(hostService.getHostsByIp(IP1)).andReturn(Collections.<Host>emptySet());
217 replay(hostService);
218
219 assertFalse(proxyArp.known(IP1));
220 }
221
222 /**
223 * Tests {@link ProxyArpManager#known(IpPrefix)} in the case where the
224 * IP address is known.
225 * Verifies the method returns true.
226 */
227 @Test
228 public void testKnown() {
229 Host host1 = createMock(Host.class);
230 Host host2 = createMock(Host.class);
231
232 expect(hostService.getHostsByIp(IP1))
233 .andReturn(Sets.newHashSet(host1, host2));
234 replay(hostService);
235
236 assertTrue(proxyArp.known(IP1));
237 }
238
239 /**
240 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
241 * destination host is known.
242 * Verifies the correct ARP reply is sent out the correct port.
243 */
244 @Test
245 public void testReplyKnown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700246 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN1, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700247 Collections.singleton(IP1));
248
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700249 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700250 Collections.singleton(IP2));
251
252 expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
253 .andReturn(Collections.singleton(replyer));
254 expect(hostService.getHost(HID2)).andReturn(requestor);
255
256 replay(hostService);
257
258 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
259
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700260 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700261
262 assertEquals(1, packetService.packets.size());
263 Ethernet arpReply = buildArp(ARP.OP_REPLY, MAC1, MAC2, IP1, IP2);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700264 verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0));
Jonathan Hart704ca142014-10-09 09:34:39 -0700265 }
266
267 /**
268 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
269 * destination host is not known.
270 * Verifies the ARP request is flooded out the correct edge ports.
271 */
272 @Test
273 public void testReplyUnknown() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700274 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700275 Collections.singleton(IP2));
276
277 expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
278 .andReturn(Collections.<Host>emptySet());
279 expect(hostService.getHost(HID2)).andReturn(requestor);
280
281 replay(hostService);
282
283 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
284
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700285 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700286
287 verifyFlood(arpRequest);
288 }
289
290 /**
291 * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
292 * destination host is known for that IP address, but is not on the same
293 * VLAN as the source host.
294 * Verifies the ARP request is flooded out the correct edge ports.
295 */
296 @Test
297 public void testReplyDifferentVlan() {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700298 Host replyer = new DefaultHost(PID, HID1, MAC1, VLAN2, getLocation(4),
Jonathan Hart704ca142014-10-09 09:34:39 -0700299 Collections.singleton(IP1));
300
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700301 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, getLocation(5),
Jonathan Hart704ca142014-10-09 09:34:39 -0700302 Collections.singleton(IP2));
303
304 expect(hostService.getHostsByIp(IpPrefix.valueOf(IP1.toOctets())))
305 .andReturn(Collections.singleton(replyer));
306 expect(hostService.getHost(HID2)).andReturn(requestor);
307
308 replay(hostService);
309
310 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, IP2, IP1);
311
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700312 proxyArp.reply(arpRequest, getLocation(5));
Jonathan Hart704ca142014-10-09 09:34:39 -0700313
314 verifyFlood(arpRequest);
315 }
316
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700317 @Test
318 public void testReplyToRequestForUs() {
319 IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24");
320 IpPrefix ourFirstIp = IpPrefix.valueOf("10.0.1.1/24");
321 IpPrefix ourSecondIp = IpPrefix.valueOf("10.0.2.1/24");
322 MacAddress ourMac = MacAddress.valueOf(1L);
323
324 Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1,
325 Collections.singleton(theirIp));
326
327 expect(hostService.getHost(HID2)).andReturn(requestor);
328 replay(hostService);
329
330 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourFirstIp);
331
332 proxyArp.reply(arpRequest, LOC1);
333
334 assertEquals(1, packetService.packets.size());
335 Ethernet arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourFirstIp, theirIp);
336 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
337
338 // Test a request for the second address on that port
339 packetService.packets.clear();
340 arpRequest = buildArp(ARP.OP_REQUEST, MAC2, null, theirIp, ourSecondIp);
341
342 proxyArp.reply(arpRequest, LOC1);
343
344 assertEquals(1, packetService.packets.size());
345 arpReply = buildArp(ARP.OP_REPLY, ourMac, MAC2, ourSecondIp, theirIp);
346 verifyPacketOut(arpReply, LOC1, packetService.packets.get(0));
347 }
348
349 @Test
350 public void testReplyExternalPortBadRequest() {
351 replay(hostService); // no further host service expectations
352
353 IpPrefix theirIp = IpPrefix.valueOf("10.0.1.254/24");
354
355 // Request for a valid external IP address but coming in the wrong port
356 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
357 IpPrefix.valueOf("10.0.3.1"));
358 proxyArp.reply(arpRequest, LOC1);
359 assertEquals(0, packetService.packets.size());
360
361 // Request for a valid internal IP address but coming in an external port
362 packetService.packets.clear();
363 arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp, IP1);
364 proxyArp.reply(arpRequest, LOC1);
365 assertEquals(0, packetService.packets.size());
366 }
367
368 @Test
369 public void testReplyToRequestFromUs() {
370 replay(hostService); // no further host service expectations
371
372 IpPrefix ourIp = IpPrefix.valueOf("10.0.1.1/24");
373 MacAddress ourMac = MacAddress.valueOf(1L);
374 IpPrefix theirIp = IpPrefix.valueOf("10.0.1.100/24");
375
376 // This is a request from something inside our network (like a BGP
377 // daemon) to an external host.
378 Ethernet arpRequest = buildArp(ARP.OP_REQUEST, ourMac, null, ourIp, theirIp);
379 proxyArp.reply(arpRequest, getLocation(5));
380
381 assertEquals(1, packetService.packets.size());
382 verifyPacketOut(arpRequest, getLocation(1), packetService.packets.get(0));
383
384 // The same request from a random external port should fail
385 packetService.packets.clear();
386 proxyArp.reply(arpRequest, getLocation(2));
387 assertEquals(0, packetService.packets.size());
388 }
389
Jonathan Hart704ca142014-10-09 09:34:39 -0700390 /**
391 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
392 * destination host is known.
393 * Verifies the correct ARP request is sent out the correct port.
394 */
395 @Test
396 public void testForwardToHost() {
397 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
398 Collections.singleton(IP1));
399
400 expect(hostService.getHost(HID1)).andReturn(host1);
401 replay(hostService);
402
403 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
404
405 proxyArp.forward(arpRequest);
406
407 assertEquals(1, packetService.packets.size());
408 OutboundPacket packet = packetService.packets.get(0);
409
410 verifyPacketOut(arpRequest, LOC1, packet);
411 }
412
413 /**
414 * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
415 * destination host is not known.
416 * Verifies the correct ARP request is flooded out the correct edge ports.
417 */
418 @Test
419 public void testForwardFlood() {
420 expect(hostService.getHost(HID1)).andReturn(null);
421 replay(hostService);
422
423 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
424
425 proxyArp.forward(arpRequest);
426
427 verifyFlood(arpRequest);
428 }
429
430 /**
431 * Verifies that the given packet was flooded out all available edge ports.
432 *
433 * @param packet the packet that was expected to be flooded
434 */
435 private void verifyFlood(Ethernet packet) {
436 assertEquals(NUM_FLOOD_PORTS, packetService.packets.size());
437
438 Collections.sort(packetService.packets,
439 new Comparator<OutboundPacket>() {
440 @Override
441 public int compare(OutboundPacket o1, OutboundPacket o2) {
442 return o1.sendThrough().uri().compareTo(o2.sendThrough().uri());
443 }
444 });
445
446 for (int i = 0; i < NUM_FLOOD_PORTS; i++) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700447 ConnectPoint cp = new ConnectPoint(getDeviceId(NUM_ADDRESS_PORTS + i + 1),
448 PortNumber.portNumber(1));
Jonathan Hart704ca142014-10-09 09:34:39 -0700449
450 OutboundPacket outboundPacket = packetService.packets.get(i);
451 verifyPacketOut(packet, cp, outboundPacket);
452 }
453 }
454
455 /**
456 * Verifies the given packet was sent out the given port.
457 *
458 * @param expected the packet that was expected to be sent
459 * @param outPort the port the packet was expected to be sent out
460 * @param actual the actual OutboundPacket to verify
461 */
462 private void verifyPacketOut(Ethernet expected, ConnectPoint outPort,
463 OutboundPacket actual) {
464 assertTrue(Arrays.equals(expected.serialize(), actual.data().array()));
465 assertEquals(1, actual.treatment().instructions().size());
466 assertEquals(outPort.deviceId(), actual.sendThrough());
467 Instruction instruction = actual.treatment().instructions().get(0);
468 assertTrue(instruction instanceof OutputInstruction);
469 assertEquals(outPort.port(), ((OutputInstruction) instruction).port());
470 }
471
472 /**
473 * Returns the device ID of the ith device.
474 *
475 * @param i device to get the ID of
476 * @return the device ID
477 */
478 private static DeviceId getDeviceId(int i) {
479 return DeviceId.deviceId("" + i);
480 }
481
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700482 private static HostLocation getLocation(int i) {
483 return new HostLocation(new ConnectPoint(getDeviceId(i), P1), 123L);
484 }
485
Jonathan Hart704ca142014-10-09 09:34:39 -0700486 /**
487 * Builds an ARP packet with the given parameters.
488 *
489 * @param opcode opcode of the ARP packet
490 * @param srcMac source MAC address
491 * @param dstMac destination MAC address, or null if this is a request
492 * @param srcIp source IP address
493 * @param dstIp destination IP address
494 * @return the ARP packet
495 */
496 private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
497 IpPrefix srcIp, IpPrefix dstIp) {
498 Ethernet eth = new Ethernet();
499
500 if (dstMac == null) {
501 eth.setDestinationMACAddress(MacAddress.BROADCAST_MAC);
502 } else {
503 eth.setDestinationMACAddress(dstMac.getAddress());
504 }
505
506 eth.setSourceMACAddress(srcMac.getAddress());
507 eth.setEtherType(Ethernet.TYPE_ARP);
508 eth.setVlanID(VLAN1.toShort());
509
510 ARP arp = new ARP();
511 arp.setOpCode(opcode);
512 arp.setProtocolType(ARP.PROTO_TYPE_IP);
513 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
514
515 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
516 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
517 arp.setSenderHardwareAddress(srcMac.getAddress());
518
519 if (dstMac == null) {
520 arp.setTargetHardwareAddress(MacAddress.ZERO_MAC_ADDRESS);
521 } else {
522 arp.setTargetHardwareAddress(dstMac.getAddress());
523 }
524
525 arp.setSenderProtocolAddress(srcIp.toOctets());
526 arp.setTargetProtocolAddress(dstIp.toOctets());
527
528 eth.setPayload(arp);
529 return eth;
530 }
531
532 /**
533 * Test PacketService implementation that simply stores OutboundPackets
534 * passed to {@link #emit(OutboundPacket)} for later verification.
535 */
536 class TestPacketService implements PacketService {
537
538 List<OutboundPacket> packets = new ArrayList<>();
539
540 @Override
541 public void addProcessor(PacketProcessor processor, int priority) {
542 }
543
544 @Override
545 public void removeProcessor(PacketProcessor processor) {
546 }
547
548 @Override
549 public void emit(OutboundPacket packet) {
550 packets.add(packet);
551 }
552 }
553}