blob: 25a2640d9e352361aa35859612449567f20be9cc [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.proxyarp.impl;
alshabibb5522ff2014-09-29 19:20:00 -070017
alshabibb5522ff2014-09-29 19:20:00 -070018import 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;
23import org.apache.felix.scr.annotations.Service;
Jonathan Harte8600eb2015-01-12 10:30:45 -080024import org.onlab.packet.ARP;
25import org.onlab.packet.Ethernet;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080026import org.onlab.packet.ICMP6;
27import org.onlab.packet.IPv6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080028import org.onlab.packet.Ip4Address;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080029import org.onlab.packet.Ip6Address;
Jonathan Harte8600eb2015-01-12 10:30:45 -080030import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.packet.VlanId;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080033import org.onlab.packet.ndp.NeighborAdvertisement;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080034import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080035import org.onlab.packet.ndp.NeighborSolicitation;
Jonathan Hart4cb39882015-08-12 23:50:55 -040036import org.onosproject.incubator.net.intf.Interface;
37import org.onosproject.incubator.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.Host;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.device.DeviceService;
Aaron Kruglikovd8123832015-07-06 14:20:25 -070041import org.onosproject.net.edge.EdgePortService;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.host.HostService;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.link.LinkService;
46import org.onosproject.net.packet.DefaultOutboundPacket;
47import org.onosproject.net.packet.InboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketService;
50import org.onosproject.net.proxyarp.ProxyArpService;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070051import org.onosproject.net.proxyarp.ProxyArpStore;
alshabibb5522ff2014-09-29 19:20:00 -070052import org.slf4j.Logger;
53
Jonathan Hart6cd2f352015-01-13 17:44:45 -080054import java.nio.ByteBuffer;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080055import java.util.Set;
56
57import static com.google.common.base.Preconditions.checkArgument;
58import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070059import static org.onlab.packet.VlanId.vlanId;
60import static org.onosproject.net.HostId.hostId;
Changhoon Yoon541ef712015-05-23 17:18:34 +090061import static org.onosproject.security.AppGuard.checkPermission;
Aaron Kruglikovd8123832015-07-06 14:20:25 -070062import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoonb856b812015-08-10 03:47:19 +090063import static org.onosproject.security.AppPermission.Type.*;
Changhoon Yoon541ef712015-05-23 17:18:34 +090064
alshabibb5522ff2014-09-29 19:20:00 -070065
alshabibb5522ff2014-09-29 19:20:00 -070066@Component(immediate = true)
67@Service
68public class ProxyArpManager implements ProxyArpService {
69
70 private final Logger log = getLogger(getClass());
71
Jonathan Hart39ee6482015-08-31 16:00:19 +020072 private static final String MAC_ADDR_NULL = "MAC address cannot be null.";
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080073 private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
Jonathan Hart39ee6482015-08-31 16:00:19 +020074 private static final String MSG_NOT_REQUEST = "Message is not an ARP or NDP request";
alshabibb5522ff2014-09-29 19:20:00 -070075
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Aaron Kruglikovd8123832015-07-06 14:20:25 -070077 protected EdgePortService edgeService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb5522ff2014-09-29 19:20:00 -070080 protected HostService hostService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected PacketService packetService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected LinkService linkService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected DeviceService deviceService;
90
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected ProxyArpStore store;
93
Jonathan Hart4cb39882015-08-12 23:50:55 -040094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected InterfaceService interfaceService;
96
Jonathan Hart39ee6482015-08-31 16:00:19 +020097 private enum Protocol {
98 ARP, NDP
99 }
100
101 private enum MessageType {
102 REQUEST, REPLY
103 }
104
alshabibb5522ff2014-09-29 19:20:00 -0700105 @Activate
106 public void activate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700107 store.setDelegate(this::sendTo);
alshabibb5522ff2014-09-29 19:20:00 -0700108 log.info("Started");
109 }
110
alshabibb5522ff2014-09-29 19:20:00 -0700111 @Deactivate
112 public void deactivate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700113 store.setDelegate(null);
alshabibb5522ff2014-09-29 19:20:00 -0700114 log.info("Stopped");
115 }
116
117 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800118 public boolean isKnown(IpAddress addr) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900119 checkPermission(PACKET_READ);
120
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700121 checkNotNull(addr, MAC_ADDR_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700122 Set<Host> hosts = hostService.getHostsByIp(addr);
123 return !hosts.isEmpty();
124 }
125
126 @Override
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700127 public void reply(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900128 checkPermission(PACKET_WRITE);
129
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700130 checkNotNull(eth, REQUEST_NULL);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800131
Jonathan Hart39ee6482015-08-31 16:00:19 +0200132 MessageContext context = createContext(eth, inPort);
133 if (context != null) {
134 replyInternal(context);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800135 }
136 }
137
Jonathan Hart39ee6482015-08-31 16:00:19 +0200138 /**
139 * Handles a request message.
140 *
141 * If the MAC address of the target is known, we can reply directly to the
142 * requestor. Otherwise, we forward the request out other ports in an
143 * attempt to find the correct host.
144 *
145 * @param context request message context to process
146 */
147 private void replyInternal(MessageContext context) {
148 checkNotNull(context);
149 checkArgument(context.type() == MessageType.REQUEST, MSG_NOT_REQUEST);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700150
Jonathan Hart39ee6482015-08-31 16:00:19 +0200151 if (hasIpAddress(context.inPort())) {
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700152 // If the request came from outside the network, only reply if it was
153 // for one of our external addresses.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700154
Jonathan Hart39ee6482015-08-31 16:00:19 +0200155 interfaceService.getInterfacesByPort(context.inPort())
Jonathan Hart4cb39882015-08-12 23:50:55 -0400156 .stream()
157 .filter(intf -> intf.ipAddresses()
158 .stream()
Jonathan Hart39ee6482015-08-31 16:00:19 +0200159 .anyMatch(ia -> ia.ipAddress().equals(context.target())))
160 .forEach(intf -> buildAndSendReply(context, intf.mac()));
Jonathan Hart4cb39882015-08-12 23:50:55 -0400161
162 // Stop here and don't proxy ARPs if the port has an IP address
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700163 return;
164 }
165
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700166 // See if we have the target host in the host store
Jonathan Hart39ee6482015-08-31 16:00:19 +0200167 Set<Host> hosts = hostService.getHostsByIp(context.target());
alshabibb5522ff2014-09-29 19:20:00 -0700168
169 Host dst = null;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200170 Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
alshabibb5522ff2014-09-29 19:20:00 -0700171
172 for (Host host : hosts) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200173 if (host.vlan().equals(context.vlan())) {
alshabibb5522ff2014-09-29 19:20:00 -0700174 dst = host;
175 break;
176 }
177 }
178
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700179 if (src != null && dst != null) {
180 // We know the target host so we can respond
Jonathan Hart39ee6482015-08-31 16:00:19 +0200181 buildAndSendReply(context, dst.mac());
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700182 return;
183 }
184
185 // If the source address matches one of our external addresses
186 // it could be a request from an internal host to an external
187 // address. Forward it over to the correct port.
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700188 boolean matched = false;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200189 Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400190 for (Interface intf : interfaces) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200191 if (intf.vlan().equals(context.vlan())) {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400192 matched = true;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200193 sendTo(context.packet(), intf.connectPoint());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400194 break;
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700195 }
196 }
197
198 if (matched) {
alshabibb5522ff2014-09-29 19:20:00 -0700199 return;
200 }
201
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700202 // The request couldn't be resolved.
203 // Flood the request on all ports except the incoming port.
Jonathan Hart39ee6482015-08-31 16:00:19 +0200204 flood(context.packet(), context.inPort());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400205 }
206
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700207 /**
Jonathan Hart39ee6482015-08-31 16:00:19 +0200208 * Builds and sends a reply message given a request context and the resolved
209 * MAC address to answer with.
210 *
211 * @param context message context of request
212 * @param targetMac MAC address to be given in the response
213 */
214 private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
215 switch (context.protocol()) {
216 case ARP:
217 sendTo(ARP.buildArpReply((Ip4Address) context.target(),
218 targetMac, context.packet()), context.inPort());
219 break;
220 case NDP:
221 sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
222 context.packet()), context.inPort());
223 break;
224 default:
225 break;
226 }
227 }
228
229 /**
230 * Outputs a packet out a specific port.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700231 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700232 * @param packet the packet to send
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700233 * @param outPort the port to send it out
234 */
235 private void sendTo(Ethernet packet, ConnectPoint outPort) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700236 sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
237 }
238
Jonathan Hart39ee6482015-08-31 16:00:19 +0200239 /**
240 * Outputs a packet out a specific port.
241 *
242 * @param outPort port to send it out
243 * @param packet packet to send
244 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700245 private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700246 if (!edgeService.isEdgePoint(outPort)) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700247 // Sanity check to make sure we don't send the packet out an
248 // internal port and create a loop (could happen due to
249 // misconfiguration).
250 return;
251 }
252
tom9a693fd2014-10-03 11:32:19 -0700253 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700254 builder.setOutput(outPort.port());
255 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200256 builder.build(), packet));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700257 }
258
259 /**
Jonathan Hart4cb39882015-08-12 23:50:55 -0400260 * Returns whether the given port has any IP addresses configured or not.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700261 *
262 * @param port the port to check
Jonathan Hart4cb39882015-08-12 23:50:55 -0400263 * @return true if the port has at least one IP address configured,
264 * otherwise false
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700265 */
Jonathan Hart4cb39882015-08-12 23:50:55 -0400266 private boolean hasIpAddress(ConnectPoint port) {
267 return interfaceService.getInterfacesByPort(port)
268 .stream()
269 .map(intf -> intf.ipAddresses())
270 .findAny()
271 .isPresent();
alshabibb5522ff2014-09-29 19:20:00 -0700272 }
273
274 @Override
Jonathan Hartf84591d2015-01-16 14:33:43 -0800275 public void forward(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900276 checkPermission(PACKET_WRITE);
277
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700278 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700279
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700280 Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200281 vlanId(eth.getVlanID())));
alshabibb5522ff2014-09-29 19:20:00 -0700282
283 if (h == null) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800284 flood(eth, inPort);
alshabibb5522ff2014-09-29 19:20:00 -0700285 } else {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700286 Host subject = hostService.getHost(hostId(eth.getSourceMAC(),
287 vlanId(eth.getVlanID())));
288 store.forward(h.location(), subject, ByteBuffer.wrap(eth.serialize()));
alshabibb5522ff2014-09-29 19:20:00 -0700289 }
alshabibb5522ff2014-09-29 19:20:00 -0700290 }
291
alshabibc274c902014-10-03 14:58:27 -0700292 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800293 public boolean handlePacket(PacketContext context) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900294 checkPermission(PACKET_WRITE);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900295
alshabibc274c902014-10-03 14:58:27 -0700296 InboundPacket pkt = context.inPacket();
297 Ethernet ethPkt = pkt.parsed();
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800298
299 if (ethPkt == null) {
300 return false;
301 }
alshabibc274c902014-10-03 14:58:27 -0700302
Jonathan Hart39ee6482015-08-31 16:00:19 +0200303 MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800304
Jonathan Hart39ee6482015-08-31 16:00:19 +0200305 if (msgContext == null) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800306 return false;
307 }
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800308
Jonathan Hart39ee6482015-08-31 16:00:19 +0200309 switch (msgContext.type()) {
310 case REPLY:
311 forward(msgContext.packet(), msgContext.inPort());
312 break;
313 case REQUEST:
314 replyInternal(msgContext);
315 break;
316 default:
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800317 return false;
318 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200319
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800320 context.block();
321 return true;
322 }
323
alshabibb5522ff2014-09-29 19:20:00 -0700324 /**
325 * Flood the arp request at all edges in the network.
Dusan Pajina22b9702015-02-12 16:25:23 +0100326 *
327 * @param request the arp request
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700328 * @param inPort the connect point the arp request was received on
alshabibb5522ff2014-09-29 19:20:00 -0700329 */
Jonathan Hartf84591d2015-01-16 14:33:43 -0800330 private void flood(Ethernet request, ConnectPoint inPort) {
alshabibb5522ff2014-09-29 19:20:00 -0700331 TrafficTreatment.Builder builder = null;
332 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
333
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700334 for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400335 if (hasIpAddress(connectPoint) || connectPoint.equals(inPort)) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700336 continue;
337 }
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700338
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700339 builder = DefaultTrafficTreatment.builder();
340 builder.setOutput(connectPoint.port());
341 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700342 builder.build(), buf));
alshabibb5522ff2014-09-29 19:20:00 -0700343 }
alshabibb5522ff2014-09-29 19:20:00 -0700344 }
345
alshabibb5522ff2014-09-29 19:20:00 -0700346 /**
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800347 * Builds an Neighbor Discovery reply based on a request.
348 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700349 * @param srcIp the IP address to use as the reply source
350 * @param srcMac the MAC address to use as the reply source
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800351 * @param request the Neighbor Solicitation request we got
352 * @return an Ethernet frame containing the Neighbor Advertisement reply
353 */
354 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
355 Ethernet request) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800356 Ethernet eth = new Ethernet();
357 eth.setDestinationMACAddress(request.getSourceMAC());
358 eth.setSourceMACAddress(srcMac);
359 eth.setEtherType(Ethernet.TYPE_IPV6);
360 eth.setVlanID(request.getVlanID());
361
362 IPv6 requestIp = (IPv6) request.getPayload();
363 IPv6 ipv6 = new IPv6();
364 ipv6.setSourceAddress(srcIp.toOctets());
365 ipv6.setDestinationAddress(requestIp.getSourceAddress());
366 ipv6.setHopLimit((byte) 255);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800367
368 ICMP6 icmp6 = new ICMP6();
369 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
370 icmp6.setIcmpCode((byte) 0);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800371
372 NeighborAdvertisement nadv = new NeighborAdvertisement();
Dusan Pajina22b9702015-02-12 16:25:23 +0100373 nadv.setTargetAddress(srcIp.toOctets());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800374 nadv.setSolicitedFlag((byte) 1);
375 nadv.setOverrideFlag((byte) 1);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800376 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700377 srcMac.toBytes());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800378
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800379 icmp6.setPayload(nadv);
Dusan Pajina22b9702015-02-12 16:25:23 +0100380 ipv6.setPayload(icmp6);
381 eth.setPayload(ipv6);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800382 return eth;
383 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200384
385 /**
386 * Attempts to create a MessageContext for the given Ethernet frame. If the
387 * frame is a valid ARP or NDP request or response, a context will be
388 * created.
389 *
390 * @param eth input Ethernet frame
391 * @param inPort in port
392 * @return MessageContext if the packet was ARP or NDP, otherwise null
393 */
394 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
395 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
396 return createArpContext(eth, inPort);
397 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
398 return createNdpContext(eth, inPort);
399 }
400
401 return null;
402 }
403
404 /**
405 * Extracts context information from ARP packets.
406 *
407 * @param eth input Ethernet frame that is thought to be ARP
408 * @param inPort in port
409 * @return MessageContext object if the packet was a valid ARP packet,
410 * otherwise null
411 */
412 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
413 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
414 return null;
415 }
416
417 ARP arp = (ARP) eth.getPayload();
418
419 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
420 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
421
422 MessageType type;
423 if (arp.getOpCode() == ARP.OP_REQUEST) {
424 type = MessageType.REQUEST;
425 } else if (arp.getOpCode() == ARP.OP_REPLY) {
426 type = MessageType.REPLY;
427 } else {
428 return null;
429 }
430
431 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
432 }
433
434 /**
435 * Extracts context information from NDP packets.
436 *
437 * @param eth input Ethernet frame that is thought to be NDP
438 * @param inPort in port
439 * @return MessageContext object if the packet was a valid NDP packet,
440 * otherwise null
441 */
442 private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
443 if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
444 return null;
445 }
446 IPv6 ipv6 = (IPv6) eth.getPayload();
447
448 if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
449 return null;
450 }
451 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
452
453 IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
454 IpAddress target = null;
455
456 MessageType type;
457 if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
458 type = MessageType.REQUEST;
459 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
460 target = Ip6Address.valueOf(nsol.getTargetAddress());
461 } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
462 type = MessageType.REPLY;
463 } else {
464 return null;
465 }
466
467 return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
468 }
469
470 /**
471 * Provides context information for a particular ARP or NDP message, with
472 * a unified interface to access data regardless of protocol.
473 */
474 private class MessageContext {
475 private Protocol protocol;
476 private MessageType type;
477
478 private IpAddress target;
479 private IpAddress sender;
480
481 private Ethernet eth;
482 private ConnectPoint inPort;
483
484
485 public MessageContext(Ethernet eth, ConnectPoint inPort,
486 Protocol protocol, MessageType type,
487 IpAddress target, IpAddress sender) {
488 this.eth = eth;
489 this.inPort = inPort;
490 this.protocol = protocol;
491 this.type = type;
492 this.target = target;
493 this.sender = sender;
494 }
495
496 public ConnectPoint inPort() {
497 return inPort;
498 }
499
500 public Ethernet packet() {
501 return eth;
502 }
503
504 public Protocol protocol() {
505 return protocol;
506 }
507
508 public MessageType type() {
509 return type;
510 }
511
512 public VlanId vlan() {
513 return VlanId.vlanId(eth.getVlanID());
514 }
515
516 public MacAddress srcMac() {
517 return MacAddress.valueOf(eth.getSourceMACAddress());
518 }
519
520 public IpAddress target() {
521 return target;
522 }
523
524 public IpAddress sender() {
525 return sender;
526 }
527 }
alshabibb5522ff2014-09-29 19:20:00 -0700528}