blob: 64693ead463d7761c2d64f3b7d32b1f24aa3439c [file] [log] [blame]
Andreas Papazoisa9964ea2016-01-08 15:58:22 +02001/*
Andreas Papazoise6aebaa2016-05-26 15:25:51 +03002 * Copyright 2016-present Open Networking Laboratory
Andreas Papazoisa9964ea2016-01-08 15:58:22 +02003 *
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.sdxl3;
17
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020023import org.onlab.packet.ARP;
24import org.onlab.packet.Ethernet;
25import org.onlab.packet.ICMP6;
26import org.onlab.packet.IPv6;
27import org.onlab.packet.Ip4Address;
28import org.onlab.packet.Ip6Address;
29import org.onlab.packet.IpAddress;
30import org.onlab.packet.MacAddress;
31import org.onlab.packet.VlanId;
32import org.onlab.packet.ndp.NeighborAdvertisement;
33import org.onlab.packet.ndp.NeighborDiscoveryOptions;
34import org.onlab.packet.ndp.NeighborSolicitation;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020037import org.onosproject.incubator.net.intf.Interface;
38import org.onosproject.incubator.net.intf.InterfaceService;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.Host;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030041import org.onosproject.net.config.NetworkConfigService;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020042import org.onosproject.net.edge.EdgePortService;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.host.HostService;
46import org.onosproject.net.packet.DefaultOutboundPacket;
47import org.onosproject.net.packet.InboundPacket;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030048import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020050import org.onosproject.net.packet.PacketService;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030051import org.onosproject.routing.RoutingService;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020052import org.onosproject.routing.config.BgpConfig;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020055
56import java.nio.ByteBuffer;
57import java.util.Set;
58import java.util.stream.Collectors;
59
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020060import static com.google.common.base.Preconditions.checkNotNull;
61import static org.onosproject.net.HostId.hostId;
62import static org.onosproject.security.AppGuard.checkPermission;
63import static org.onosproject.security.AppPermission.Type.PACKET_WRITE;
64
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030065/**
66 * Proxy-ARP functionality adapted to SDX-L3.
67 */
68@Component(immediate = true, enabled = false)
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020069public class SdxL3ArpHandler {
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020070 private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030071
72 private Logger log = LoggerFactory.getLogger(getClass());
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected CoreService coreService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected InterfaceService interfaceService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected EdgePortService edgeService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected HostService hostService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected PacketService packetService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected NetworkConfigService networkConfigService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected SdxL3PeerService sdxL3PeerService;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020094
95 private BgpConfig bgpConfig;
Andreas Papazoise6aebaa2016-05-26 15:25:51 +030096 private InternalPacketProcessor processor = null;
Andreas Papazoisa9964ea2016-01-08 15:58:22 +020097
98 private enum Protocol {
99 ARP, NDP
100 }
101
102 private enum MessageType {
103 REQUEST, REPLY
104 }
105
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300106 @Activate
107 public void activate() {
108 ApplicationId routerAppId = coreService.getAppId(RoutingService.ROUTER_APP_ID);
109 bgpConfig = networkConfigService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
110 processor = new InternalPacketProcessor();
111 packetService.addProcessor(processor, PacketProcessor.director(2));
112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
117 packetService.removeProcessor(processor);
118 log.info("Stopped");
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200119 }
120
121 /**
122 * Processes incoming ARP packets.
123 *
124 * @param pkt incoming packet
125 */
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300126 protected void processPacketIn(InboundPacket pkt) {
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200127 checkPermission(PACKET_WRITE);
128
129 Ethernet eth = pkt.parsed();
130 checkNotNull(eth, REQUEST_NULL);
131
132 ConnectPoint inPort = pkt.receivedFrom();
133 MessageContext context = createContext(eth, inPort);
134 if (context != null) {
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300135 if (context.type() == MessageType.REQUEST) {
136 handleRequest(context);
137 } else if (context.type() == MessageType.REPLY) {
138 handleReply(context);
139 }
140 }
141 }
142
143 /**
144 * Handles a reply message only in the case it concerns a reply from an
145 * internal speaker to external peer where VLAN translation is necessary.
146 *
147 * @param context reply message context to process
148 */
149 private void handleReply(MessageContext context) {
150 if (fromPeerToSpeaker(context)) {
151 translateVlanAndSendToSpeaker(context);
152 } else if (fromPeerToPeer(context)) {
153 translateVlanAndSendToPeer(context);
154 }
155 }
156
157 private boolean fromPeerToSpeaker(MessageContext context) {
158 return sdxL3PeerService != null &&
159 isPeerAddress(context.sender()) &&
160 !interfaceService.getInterfacesByIp(context.target()).isEmpty();
161 }
162
163 /**
164 * Makes the VLAN translation if necessary and sends the packet at the port
165 * configured for the speaker.
166 *
167 * @param context reply message context to process
168 */
169 private void translateVlanAndSendToSpeaker(MessageContext context) {
170 BgpConfig.BgpSpeakerConfig speaker =
171 bgpConfig.getSpeakerFromPeer(context.sender());
172 if (speaker != null) {
173 Interface peeringInterface = sdxL3PeerService.getInterfaceForPeer(context.sender());
174 if (context.vlan().equals(peeringInterface.vlan())) {
175 context.setVlan(speaker.vlan());
176 sendTo(context.packet(), speaker.connectPoint());
177 }
178 }
179 }
180
181 /**
182 * Makes the VLAN translation if necessary and sends the packet at the port
183 * configured for the peer.
184 *
185 * @param context reply message context to process
186 */
187 private void translateVlanAndSendToPeer(MessageContext context) {
188 Interface interfaceForPeer = sdxL3PeerService.getInterfaceForPeer(context.target());
189 if (interfaceForPeer != null) {
190 context.setVlan(interfaceForPeer.vlan());
191 sendTo(context.packet(), interfaceForPeer.connectPoint());
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200192 }
193 }
194
195 /**
196 * Handles a request message also when it concerns an SDX peering address.
197 *
198 * If the MAC address of the target is known, we can reply directly to the
199 * requestor. Otherwise, we forward the request out other ports in an
200 * attempt to find the correct host.
201 *
202 * @param context request message context to process
203 */
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300204 private void handleRequest(MessageContext context) {
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200205 if (hasIpAddress(context.inPort())) {
206 // If the request came from outside the network, only reply if it was
207 // for one of our external addresses.
208
209 interfaceService.getInterfacesByPort(context.inPort())
210 .stream()
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300211 .filter(intf -> intf.ipAddressesList()
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200212 .stream()
213 .anyMatch(ia -> ia.ipAddress().equals(context.target())))
214 .forEach(intf -> buildAndSendReply(context, intf.mac()));
215
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300216 if (!fromPeerToPeer(context)) {
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200217 // Only care about requests from/towards external BGP peers
218 return;
219 }
220 }
221
222 // See if we have the target host in the host store
223 Set<Host> hosts = hostService.getHostsByIp(context.target());
224
225 Host dst = null;
226 Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
227
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300228 // If the request concerns an external BGP peer address and an internal
229 // BGP speaker or is between external BGP peers, VLAN translation may be
230 // necessary on the ARP request.
231 if (fromSpeakerToPeer(context) || fromPeerToPeer(context)) {
232 translateVlanAndSendToPeer(context);
233 return;
234 }
235
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200236 for (Host host : hosts) {
237 if (host.vlan().equals(context.vlan())) {
238 dst = host;
239 break;
240 }
241 }
242
243 if (src != null && dst != null) {
244 // We know the target host so we can respond
245 buildAndSendReply(context, dst.mac());
246 return;
247 }
248
249 // If the source address matches one of our external addresses
250 // it could be a request from an internal host to an external
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200251 // address. Forward it over to the correct connectPoint.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200252 boolean matched = false;
253 Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
254 for (Interface intf : interfaces) {
255 if (intf.vlan().equals(context.vlan())) {
256 matched = true;
257 sendTo(context.packet(), intf.connectPoint());
258 break;
259 }
260 }
261
262 if (matched) {
263 return;
264 }
265
266 // If the packets has a vlanId look if there are some other
267 // interfaces in the configuration on the same vlan and broadcast
268 // the packet out just of through those interfaces.
269 VlanId vlanId = context.vlan();
270
271 Set<Interface> filteredVlanInterfaces =
272 filterVlanInterfacesNoIp(interfaceService.getInterfacesByVlan(vlanId));
273
274 if (vlanId != null
275 && !vlanId.equals(VlanId.NONE)
276 && confContainsVlans(vlanId, context.inPort())) {
277 vlanFlood(context.packet(), filteredVlanInterfaces, context.inPort);
278 return;
279 }
280
281 // The request couldn't be resolved.
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200282 // Flood the request on all ports except the incoming connectPoint.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200283 flood(context.packet(), context.inPort());
284 }
285
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300286 private boolean fromPeerToPeer(MessageContext context) {
287 return isPeerAddress(context.sender()) && isPeerAddress(context.target());
288 }
289
290 private boolean fromSpeakerToPeer(MessageContext context) {
291 return sdxL3PeerService != null &&
292 isPeerAddress(context.target()) &&
293 !interfaceService.getInterfacesByIp(context.sender()).isEmpty();
294 }
295
296 /**
297 * Makes the VLAN translation if necessary on the packet.
298 *
299 * @param context request message context to process
300 */
301 private VlanId getDestinationPeerVlan(MessageContext context) {
302 return sdxL3PeerService.getInterfaceForPeer(context.target()).vlan();
303 }
304
305 /**
306 * Controls whether an IP address is configured for an external peer.
307 *
308 * @param ip the examined IP address
309 * @return result of the control
310 */
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200311 private boolean isPeerAddress(IpAddress ip) {
312 return bgpConfig.bgpSpeakers()
313 .stream()
314 .flatMap(speaker -> speaker.peers().stream())
315 .anyMatch(peerAddress -> peerAddress.equals(ip));
316 }
317
318 private Set<Interface> filterVlanInterfacesNoIp(Set<Interface> vlanInterfaces) {
319 return vlanInterfaces
320 .stream()
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300321 .filter(intf -> intf.ipAddressesList().isEmpty())
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200322 .collect(Collectors.toSet());
323 }
324
325 /**
326 * States if the interface configuration contains more than one interface configured
327 * on a specific vlan, including the interface passed as argument.
328 *
329 * @param vlanId the vlanid to look for in the interface configuration
330 * @param connectPoint the connect point to exclude from the search
331 * @return true if interfaces are found. False otherwise
332 */
333 private boolean confContainsVlans(VlanId vlanId, ConnectPoint connectPoint) {
334 Set<Interface> vlanInterfaces = interfaceService.getInterfacesByVlan(vlanId);
335 return interfaceService.getInterfacesByVlan(vlanId)
336 .stream()
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300337 .anyMatch(intf -> intf.connectPoint().equals(connectPoint) &&
338 intf.ipAddressesList().isEmpty())
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200339 && vlanInterfaces.size() > 1;
340 }
341
342 /**
343 * Builds and sends a reply message given a request context and the resolved
344 * MAC address to answer with.
345 *
346 * @param context message context of request
347 * @param targetMac MAC address to be given in the response
348 */
349 private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
350 switch (context.protocol()) {
351 case ARP:
352 sendTo(ARP.buildArpReply((Ip4Address) context.target(),
353 targetMac, context.packet()), context.inPort());
354 break;
355 case NDP:
356 sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
357 context.packet()), context.inPort());
358 break;
359 default:
360 break;
361 }
362 }
363
364 /**
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200365 * Outputs a packet out a specific connectPoint.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200366 *
367 * @param packet the packet to send
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200368 * @param outPort the connectPoint to send it out
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200369 */
370 private void sendTo(Ethernet packet, ConnectPoint outPort) {
371 sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
372 }
373
374 /**
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200375 * Outputs a packet out a specific connectPoint.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200376 *
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200377 * @param outPort connectPoint to send it out
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200378 * @param packet packet to send
379 */
380 private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
381 if (!edgeService.isEdgePoint(outPort)) {
382 // Sanity check to make sure we don't send the packet out an
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200383 // internal connectPoint and create a loop (could happen due to
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200384 // misconfiguration).
385 return;
386 }
387
388 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
389 builder.setOutput(outPort.port());
390 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
391 builder.build(), packet));
392 }
393
394 /**
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200395 * Returns whether the given connectPoint has any IP addresses configured or not.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200396 *
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200397 * @param connectPoint the connectPoint to check
398 * @return true if the connectPoint has at least one IP address configured,
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200399 * false otherwise
400 */
401 private boolean hasIpAddress(ConnectPoint connectPoint) {
402 return interfaceService.getInterfacesByPort(connectPoint)
403 .stream()
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300404 .flatMap(intf -> intf.ipAddressesList().stream())
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200405 .findAny()
406 .isPresent();
407 }
408
409 /**
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200410 * Returns whether the given connectPoint has any VLAN configured or not.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200411 *
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200412 * @param connectPoint the connectPoint to check
413 * @return true if the connectPoint has at least one VLAN configured,
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200414 * false otherwise
415 */
416 private boolean hasVlan(ConnectPoint connectPoint) {
417 return interfaceService.getInterfacesByPort(connectPoint)
418 .stream()
419 .filter(intf -> !intf.vlan().equals(VlanId.NONE))
420 .findAny()
421 .isPresent();
422 }
423
424 /**
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300425 * Floods the arp request at all edges on a specifc VLAN.
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200426 *
427 * @param request the arp request
428 * @param dsts the destination interfaces
429 * @param inPort the connect point the arp request was received on
430 */
431 private void vlanFlood(Ethernet request, Set<Interface> dsts, ConnectPoint inPort) {
432 TrafficTreatment.Builder builder = null;
433 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
434
435 for (Interface intf : dsts) {
436 ConnectPoint cPoint = intf.connectPoint();
437 if (cPoint.equals(inPort)) {
438 continue;
439 }
440
441 builder = DefaultTrafficTreatment.builder();
442 builder.setOutput(cPoint.port());
443 packetService.emit(new DefaultOutboundPacket(cPoint.deviceId(),
444 builder.build(), buf));
445 }
446 }
447
448 /**
449 * Flood the arp request at all edges in the network.
450 *
451 * @param request the arp request
452 * @param inPort the connect point the arp request was received on
453 */
454 private void flood(Ethernet request, ConnectPoint inPort) {
455 TrafficTreatment.Builder builder = null;
456 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
457
458 for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
459 if (hasIpAddress(connectPoint)
460 || hasVlan(connectPoint)
461 || connectPoint.equals(inPort)) {
462 continue;
463 }
464
465 builder = DefaultTrafficTreatment.builder();
466 builder.setOutput(connectPoint.port());
467 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
468 builder.build(), buf));
469 }
470 }
471
472 /**
473 * Builds an Neighbor Discovery reply based on a request.
474 *
475 * @param srcIp the IP address to use as the reply source
476 * @param srcMac the MAC address to use as the reply source
477 * @param request the Neighbor Solicitation request we got
478 * @return an Ethernet frame containing the Neighbor Advertisement reply
479 */
480 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
481 Ethernet request) {
482 Ethernet eth = new Ethernet();
483 eth.setDestinationMACAddress(request.getSourceMAC());
484 eth.setSourceMACAddress(srcMac);
485 eth.setEtherType(Ethernet.TYPE_IPV6);
486 eth.setVlanID(request.getVlanID());
487
488 IPv6 requestIp = (IPv6) request.getPayload();
489 IPv6 ipv6 = new IPv6();
490 ipv6.setSourceAddress(srcIp.toOctets());
491 ipv6.setDestinationAddress(requestIp.getSourceAddress());
492 ipv6.setHopLimit((byte) 255);
493
494 ICMP6 icmp6 = new ICMP6();
495 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
496 icmp6.setIcmpCode((byte) 0);
497
498 NeighborAdvertisement nadv = new NeighborAdvertisement();
499 nadv.setTargetAddress(srcIp.toOctets());
500 nadv.setSolicitedFlag((byte) 1);
501 nadv.setOverrideFlag((byte) 1);
502 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
503 srcMac.toBytes());
504
505 icmp6.setPayload(nadv);
506 ipv6.setPayload(icmp6);
507 eth.setPayload(ipv6);
508 return eth;
509 }
510
511 /**
512 * Attempts to create a MessageContext for the given Ethernet frame. If the
513 * frame is a valid ARP or NDP request or response, a context will be
514 * created.
515 *
516 * @param eth input Ethernet frame
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200517 * @param inPort in connectPoint
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200518 * @return MessageContext if the packet was ARP or NDP, otherwise null
519 */
520 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
521 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
522 return createArpContext(eth, inPort);
523 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
524 return createNdpContext(eth, inPort);
525 }
526
527 return null;
528 }
529
530 /**
531 * Extracts context information from ARP packets.
532 *
533 * @param eth input Ethernet frame that is thought to be ARP
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200534 * @param inPort in connectPoint
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200535 * @return MessageContext object if the packet was a valid ARP packet,
536 * otherwise null
537 */
538 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
539 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
540 return null;
541 }
542
543 ARP arp = (ARP) eth.getPayload();
544
545 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
546 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
547
548 MessageType type;
549 if (arp.getOpCode() == ARP.OP_REQUEST) {
550 type = MessageType.REQUEST;
551 } else if (arp.getOpCode() == ARP.OP_REPLY) {
552 type = MessageType.REPLY;
553 } else {
554 return null;
555 }
556
557 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
558 }
559
560 /**
561 * Extracts context information from NDP packets.
562 *
563 * @param eth input Ethernet frame that is thought to be NDP
Andreas Papazoisc2c45012016-01-20 14:26:11 +0200564 * @param inPort in connectPoint
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200565 * @return MessageContext object if the packet was a valid NDP packet,
566 * otherwise null
567 */
568 private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
569 if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
570 return null;
571 }
572 IPv6 ipv6 = (IPv6) eth.getPayload();
573
574 if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
575 return null;
576 }
577 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
578
579 IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300580 IpAddress target = Ip6Address.valueOf(ipv6.getDestinationAddress());
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200581
582 MessageType type;
583 if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
584 type = MessageType.REQUEST;
585 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
586 target = Ip6Address.valueOf(nsol.getTargetAddress());
587 } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
588 type = MessageType.REPLY;
589 } else {
590 return null;
591 }
592
593 return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
594 }
595
596 /**
597 * Provides context information for a particular ARP or NDP message, with
598 * a unified interface to access data regardless of protocol.
599 */
600 private class MessageContext {
601 private Protocol protocol;
602 private MessageType type;
603
604 private IpAddress target;
605 private IpAddress sender;
606
607 private Ethernet eth;
608 private ConnectPoint inPort;
609
610
611 public MessageContext(Ethernet eth, ConnectPoint inPort,
612 Protocol protocol, MessageType type,
613 IpAddress target, IpAddress sender) {
614 this.eth = eth;
615 this.inPort = inPort;
616 this.protocol = protocol;
617 this.type = type;
618 this.target = target;
619 this.sender = sender;
620 }
621
622 public ConnectPoint inPort() {
623 return inPort;
624 }
625
626 public Ethernet packet() {
627 return eth;
628 }
629
630 public Protocol protocol() {
631 return protocol;
632 }
633
634 public MessageType type() {
635 return type;
636 }
637
638 public VlanId vlan() {
639 return VlanId.vlanId(eth.getVlanID());
640 }
641
642 public MacAddress srcMac() {
643 return MacAddress.valueOf(eth.getSourceMACAddress());
644 }
645
646 public IpAddress target() {
647 return target;
648 }
649
650 public IpAddress sender() {
651 return sender;
652 }
Andreas Papazoise6aebaa2016-05-26 15:25:51 +0300653
654 public void setVlan(VlanId vlanId) {
655 this.eth.setVlanID(vlanId.toShort());
656 }
657 }
658
659 private class InternalPacketProcessor implements PacketProcessor {
660 @Override
661 public void process(PacketContext context) {
662
663 if (context.isHandled()) {
664 return;
665 }
666
667 InboundPacket pkt = context.inPacket();
668 Ethernet ethernet = pkt.parsed();
669 if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
670 processPacketIn(pkt);
671 }
672 }
Andreas Papazoisa9964ea2016-01-08 15:58:22 +0200673 }
674
675}