blob: 3937e9c2566ac1150a94917985e6eb5b0ab282fb [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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;
Luca Pretef70d3992015-10-30 16:24:14 -070056import java.util.stream.Collectors;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080057
58import static com.google.common.base.Preconditions.checkArgument;
59import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070060import static org.onlab.packet.VlanId.vlanId;
61import static org.onosproject.net.HostId.hostId;
Changhoon Yoon541ef712015-05-23 17:18:34 +090062import static org.onosproject.security.AppGuard.checkPermission;
Aaron Kruglikovd8123832015-07-06 14:20:25 -070063import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoonb856b812015-08-10 03:47:19 +090064import static org.onosproject.security.AppPermission.Type.*;
Changhoon Yoon541ef712015-05-23 17:18:34 +090065
Jonathan Hartc4f681c2016-09-09 07:14:25 -070066/**
67 * Implementation of the proxy ARP service.
68 *
69 * @deprecated in Hummingbird release
70 */
71@Deprecated
alshabibb5522ff2014-09-29 19:20:00 -070072@Component(immediate = true)
73@Service
74public class ProxyArpManager implements ProxyArpService {
75
76 private final Logger log = getLogger(getClass());
77
Jonathan Hart39ee6482015-08-31 16:00:19 +020078 private static final String MAC_ADDR_NULL = "MAC address cannot be null.";
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080079 private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
Jonathan Hart39ee6482015-08-31 16:00:19 +020080 private static final String MSG_NOT_REQUEST = "Message is not an ARP or NDP request";
alshabibb5522ff2014-09-29 19:20:00 -070081
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Aaron Kruglikovd8123832015-07-06 14:20:25 -070083 protected EdgePortService edgeService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb5522ff2014-09-29 19:20:00 -070086 protected HostService hostService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected PacketService packetService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected LinkService linkService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected ProxyArpStore store;
99
Jonathan Hart4cb39882015-08-12 23:50:55 -0400100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected InterfaceService interfaceService;
102
Jonathan Hart39ee6482015-08-31 16:00:19 +0200103 private enum Protocol {
104 ARP, NDP
105 }
106
107 private enum MessageType {
108 REQUEST, REPLY
109 }
110
alshabibb5522ff2014-09-29 19:20:00 -0700111 @Activate
112 public void activate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700113 store.setDelegate(this::sendTo);
alshabibb5522ff2014-09-29 19:20:00 -0700114 log.info("Started");
115 }
116
alshabibb5522ff2014-09-29 19:20:00 -0700117 @Deactivate
118 public void deactivate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700119 store.setDelegate(null);
alshabibb5522ff2014-09-29 19:20:00 -0700120 log.info("Stopped");
121 }
122
123 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800124 public boolean isKnown(IpAddress addr) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900125 checkPermission(PACKET_READ);
126
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700127 checkNotNull(addr, MAC_ADDR_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700128 Set<Host> hosts = hostService.getHostsByIp(addr);
129 return !hosts.isEmpty();
130 }
131
132 @Override
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700133 public void reply(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900134 checkPermission(PACKET_WRITE);
135
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700136 checkNotNull(eth, REQUEST_NULL);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800137
Jonathan Hart39ee6482015-08-31 16:00:19 +0200138 MessageContext context = createContext(eth, inPort);
139 if (context != null) {
140 replyInternal(context);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800141 }
142 }
143
Jonathan Hart39ee6482015-08-31 16:00:19 +0200144 /**
145 * Handles a request message.
146 *
147 * If the MAC address of the target is known, we can reply directly to the
148 * requestor. Otherwise, we forward the request out other ports in an
149 * attempt to find the correct host.
150 *
151 * @param context request message context to process
152 */
153 private void replyInternal(MessageContext context) {
154 checkNotNull(context);
155 checkArgument(context.type() == MessageType.REQUEST, MSG_NOT_REQUEST);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700156
Jonathan Hart39ee6482015-08-31 16:00:19 +0200157 if (hasIpAddress(context.inPort())) {
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700158 // If the request came from outside the network, only reply if it was
159 // for one of our external addresses.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700160
Jonathan Hart39ee6482015-08-31 16:00:19 +0200161 interfaceService.getInterfacesByPort(context.inPort())
Jonathan Hart4cb39882015-08-12 23:50:55 -0400162 .stream()
163 .filter(intf -> intf.ipAddresses()
164 .stream()
Jonathan Hart39ee6482015-08-31 16:00:19 +0200165 .anyMatch(ia -> ia.ipAddress().equals(context.target())))
166 .forEach(intf -> buildAndSendReply(context, intf.mac()));
Jonathan Hart4cb39882015-08-12 23:50:55 -0400167
168 // Stop here and don't proxy ARPs if the port has an IP address
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700169 return;
170 }
171
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700172 // See if we have the target host in the host store
Jonathan Hart39ee6482015-08-31 16:00:19 +0200173 Set<Host> hosts = hostService.getHostsByIp(context.target());
alshabibb5522ff2014-09-29 19:20:00 -0700174
175 Host dst = null;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200176 Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
alshabibb5522ff2014-09-29 19:20:00 -0700177
178 for (Host host : hosts) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200179 if (host.vlan().equals(context.vlan())) {
alshabibb5522ff2014-09-29 19:20:00 -0700180 dst = host;
181 break;
182 }
183 }
184
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700185 if (src != null && dst != null) {
186 // We know the target host so we can respond
Jonathan Hart39ee6482015-08-31 16:00:19 +0200187 buildAndSendReply(context, dst.mac());
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700188 return;
189 }
190
191 // If the source address matches one of our external addresses
192 // it could be a request from an internal host to an external
193 // address. Forward it over to the correct port.
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700194 boolean matched = false;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200195 Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400196 for (Interface intf : interfaces) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200197 if (intf.vlan().equals(context.vlan())) {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400198 matched = true;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200199 sendTo(context.packet(), intf.connectPoint());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400200 break;
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700201 }
202 }
203
204 if (matched) {
alshabibb5522ff2014-09-29 19:20:00 -0700205 return;
206 }
207
Luca Pretef70d3992015-10-30 16:24:14 -0700208 // If the packets has a vlanId look if there are some other
209 // interfaces in the configuration on the same vlan and broadcast
210 // the packet out just of through those interfaces.
211 VlanId vlanId = context.vlan();
212
213 Set<Interface> filteredVlanInterfaces =
214 filterVlanInterfacesNoIp(interfaceService.getInterfacesByVlan(vlanId));
215
216 if (vlanId != null
217 && !vlanId.equals(VlanId.NONE)
218 && confContainsVlans(vlanId, context.inPort())) {
219 vlanFlood(context.packet(), filteredVlanInterfaces, context.inPort);
220 return;
221 }
222
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700223 // The request couldn't be resolved.
224 // Flood the request on all ports except the incoming port.
Jonathan Hart39ee6482015-08-31 16:00:19 +0200225 flood(context.packet(), context.inPort());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400226 }
227
Luca Pretef70d3992015-10-30 16:24:14 -0700228 private Set<Interface> filterVlanInterfacesNoIp(Set<Interface> vlanInterfaces) {
229 return vlanInterfaces
230 .stream()
231 .filter(intf -> intf.ipAddresses().isEmpty())
232 .collect(Collectors.toSet());
233 }
234
235 /**
236 * States if the interface configuration contains more than one interface configured
237 * on a specific vlan, including the interface passed as argument.
238 *
239 * @param vlanId the vlanid to look for in the interface configuration
240 * @param connectPoint the connect point to exclude from the search
241 * @return true if interfaces are found. False otherwise
242 */
243 private boolean confContainsVlans(VlanId vlanId, ConnectPoint connectPoint) {
244 Set<Interface> vlanInterfaces = interfaceService.getInterfacesByVlan(vlanId);
245 return interfaceService.getInterfacesByVlan(vlanId)
246 .stream()
247 .anyMatch(intf -> intf.connectPoint().equals(connectPoint) && intf.ipAddresses().isEmpty())
248 && vlanInterfaces.size() > 1;
249 }
250
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700251 /**
Jonathan Hart39ee6482015-08-31 16:00:19 +0200252 * Builds and sends a reply message given a request context and the resolved
253 * MAC address to answer with.
254 *
255 * @param context message context of request
256 * @param targetMac MAC address to be given in the response
257 */
258 private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
259 switch (context.protocol()) {
260 case ARP:
261 sendTo(ARP.buildArpReply((Ip4Address) context.target(),
262 targetMac, context.packet()), context.inPort());
263 break;
264 case NDP:
265 sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
266 context.packet()), context.inPort());
267 break;
268 default:
269 break;
270 }
271 }
272
273 /**
274 * Outputs a packet out a specific port.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700275 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700276 * @param packet the packet to send
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700277 * @param outPort the port to send it out
278 */
279 private void sendTo(Ethernet packet, ConnectPoint outPort) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700280 sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
281 }
282
Jonathan Hart39ee6482015-08-31 16:00:19 +0200283 /**
284 * Outputs a packet out a specific port.
285 *
286 * @param outPort port to send it out
287 * @param packet packet to send
288 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700289 private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700290 if (!edgeService.isEdgePoint(outPort)) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700291 // Sanity check to make sure we don't send the packet out an
292 // internal port and create a loop (could happen due to
293 // misconfiguration).
294 return;
295 }
296
tom9a693fd2014-10-03 11:32:19 -0700297 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700298 builder.setOutput(outPort.port());
299 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200300 builder.build(), packet));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700301 }
302
303 /**
Jonathan Hart4cb39882015-08-12 23:50:55 -0400304 * Returns whether the given port has any IP addresses configured or not.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700305 *
Luca Pretef70d3992015-10-30 16:24:14 -0700306 * @param connectPoint the port to check
Jonathan Hart4cb39882015-08-12 23:50:55 -0400307 * @return true if the port has at least one IP address configured,
Luca Pretef70d3992015-10-30 16:24:14 -0700308 * false otherwise
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700309 */
Luca Pretef70d3992015-10-30 16:24:14 -0700310 private boolean hasIpAddress(ConnectPoint connectPoint) {
311 return interfaceService.getInterfacesByPort(connectPoint)
Jonathan Hart4cb39882015-08-12 23:50:55 -0400312 .stream()
Luca Pretef70d3992015-10-30 16:24:14 -0700313 .flatMap(intf -> intf.ipAddresses().stream())
314 .findAny()
315 .isPresent();
316 }
317
318 /**
319 * Returns whether the given port has any VLAN configured or not.
320 *
321 * @param connectPoint the port to check
322 * @return true if the port has at least one VLAN configured,
323 * false otherwise
324 */
325 private boolean hasVlan(ConnectPoint connectPoint) {
326 return interfaceService.getInterfacesByPort(connectPoint)
327 .stream()
328 .filter(intf -> !intf.vlan().equals(VlanId.NONE))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400329 .findAny()
330 .isPresent();
alshabibb5522ff2014-09-29 19:20:00 -0700331 }
332
333 @Override
Jonathan Hartf84591d2015-01-16 14:33:43 -0800334 public void forward(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900335 checkPermission(PACKET_WRITE);
336
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700337 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700338
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700339 Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200340 vlanId(eth.getVlanID())));
alshabibb5522ff2014-09-29 19:20:00 -0700341
342 if (h == null) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800343 flood(eth, inPort);
alshabibb5522ff2014-09-29 19:20:00 -0700344 } else {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700345 Host subject = hostService.getHost(hostId(eth.getSourceMAC(),
346 vlanId(eth.getVlanID())));
347 store.forward(h.location(), subject, ByteBuffer.wrap(eth.serialize()));
alshabibb5522ff2014-09-29 19:20:00 -0700348 }
alshabibb5522ff2014-09-29 19:20:00 -0700349 }
350
alshabibc274c902014-10-03 14:58:27 -0700351 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800352 public boolean handlePacket(PacketContext context) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900353 checkPermission(PACKET_WRITE);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900354
alshabibc274c902014-10-03 14:58:27 -0700355 InboundPacket pkt = context.inPacket();
356 Ethernet ethPkt = pkt.parsed();
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800357
358 if (ethPkt == null) {
359 return false;
360 }
alshabibc274c902014-10-03 14:58:27 -0700361
Jonathan Hart39ee6482015-08-31 16:00:19 +0200362 MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800363
Jonathan Hart39ee6482015-08-31 16:00:19 +0200364 if (msgContext == null) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800365 return false;
366 }
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800367
Jonathan Hart39ee6482015-08-31 16:00:19 +0200368 switch (msgContext.type()) {
369 case REPLY:
370 forward(msgContext.packet(), msgContext.inPort());
371 break;
372 case REQUEST:
373 replyInternal(msgContext);
374 break;
375 default:
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800376 return false;
377 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200378
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800379 context.block();
380 return true;
381 }
382
alshabibb5522ff2014-09-29 19:20:00 -0700383 /**
Luca Pretef70d3992015-10-30 16:24:14 -0700384 * Flood the arp request at all edges on a specifc VLAN.
385 *
386 * @param request the arp request
387 * @param dsts the destination interfaces
388 * @param inPort the connect point the arp request was received on
389 */
390 private void vlanFlood(Ethernet request, Set<Interface> dsts, ConnectPoint inPort) {
391 TrafficTreatment.Builder builder = null;
392 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
393
394 for (Interface intf : dsts) {
395 ConnectPoint cPoint = intf.connectPoint();
396 if (cPoint.equals(inPort)) {
397 continue;
398 }
399
400 builder = DefaultTrafficTreatment.builder();
401 builder.setOutput(cPoint.port());
402 packetService.emit(new DefaultOutboundPacket(cPoint.deviceId(),
403 builder.build(), buf));
404 }
405 }
406
407 /**
alshabibb5522ff2014-09-29 19:20:00 -0700408 * Flood the arp request at all edges in the network.
Dusan Pajina22b9702015-02-12 16:25:23 +0100409 *
410 * @param request the arp request
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700411 * @param inPort the connect point the arp request was received on
alshabibb5522ff2014-09-29 19:20:00 -0700412 */
Jonathan Hartf84591d2015-01-16 14:33:43 -0800413 private void flood(Ethernet request, ConnectPoint inPort) {
alshabibb5522ff2014-09-29 19:20:00 -0700414 TrafficTreatment.Builder builder = null;
415 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
416
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700417 for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
Luca Pretef70d3992015-10-30 16:24:14 -0700418 if (hasIpAddress(connectPoint)
419 || hasVlan(connectPoint)
420 || connectPoint.equals(inPort)) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700421 continue;
422 }
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700423
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700424 builder = DefaultTrafficTreatment.builder();
425 builder.setOutput(connectPoint.port());
426 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700427 builder.build(), buf));
alshabibb5522ff2014-09-29 19:20:00 -0700428 }
alshabibb5522ff2014-09-29 19:20:00 -0700429 }
430
alshabibb5522ff2014-09-29 19:20:00 -0700431 /**
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800432 * Builds an Neighbor Discovery reply based on a request.
433 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700434 * @param srcIp the IP address to use as the reply source
435 * @param srcMac the MAC address to use as the reply source
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800436 * @param request the Neighbor Solicitation request we got
437 * @return an Ethernet frame containing the Neighbor Advertisement reply
438 */
439 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
440 Ethernet request) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800441 Ethernet eth = new Ethernet();
442 eth.setDestinationMACAddress(request.getSourceMAC());
443 eth.setSourceMACAddress(srcMac);
444 eth.setEtherType(Ethernet.TYPE_IPV6);
445 eth.setVlanID(request.getVlanID());
446
447 IPv6 requestIp = (IPv6) request.getPayload();
448 IPv6 ipv6 = new IPv6();
449 ipv6.setSourceAddress(srcIp.toOctets());
450 ipv6.setDestinationAddress(requestIp.getSourceAddress());
451 ipv6.setHopLimit((byte) 255);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800452
453 ICMP6 icmp6 = new ICMP6();
454 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
455 icmp6.setIcmpCode((byte) 0);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800456
457 NeighborAdvertisement nadv = new NeighborAdvertisement();
Dusan Pajina22b9702015-02-12 16:25:23 +0100458 nadv.setTargetAddress(srcIp.toOctets());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800459 nadv.setSolicitedFlag((byte) 1);
460 nadv.setOverrideFlag((byte) 1);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800461 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700462 srcMac.toBytes());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800463
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800464 icmp6.setPayload(nadv);
Dusan Pajina22b9702015-02-12 16:25:23 +0100465 ipv6.setPayload(icmp6);
466 eth.setPayload(ipv6);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800467 return eth;
468 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200469
470 /**
471 * Attempts to create a MessageContext for the given Ethernet frame. If the
472 * frame is a valid ARP or NDP request or response, a context will be
473 * created.
474 *
475 * @param eth input Ethernet frame
476 * @param inPort in port
477 * @return MessageContext if the packet was ARP or NDP, otherwise null
478 */
479 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
480 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
481 return createArpContext(eth, inPort);
482 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
483 return createNdpContext(eth, inPort);
484 }
485
486 return null;
487 }
488
489 /**
490 * Extracts context information from ARP packets.
491 *
492 * @param eth input Ethernet frame that is thought to be ARP
493 * @param inPort in port
494 * @return MessageContext object if the packet was a valid ARP packet,
495 * otherwise null
496 */
497 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
498 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
499 return null;
500 }
501
502 ARP arp = (ARP) eth.getPayload();
503
504 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
505 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
506
507 MessageType type;
508 if (arp.getOpCode() == ARP.OP_REQUEST) {
509 type = MessageType.REQUEST;
510 } else if (arp.getOpCode() == ARP.OP_REPLY) {
511 type = MessageType.REPLY;
512 } else {
513 return null;
514 }
515
516 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
517 }
518
519 /**
520 * Extracts context information from NDP packets.
521 *
522 * @param eth input Ethernet frame that is thought to be NDP
523 * @param inPort in port
524 * @return MessageContext object if the packet was a valid NDP packet,
525 * otherwise null
526 */
527 private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
528 if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
529 return null;
530 }
531 IPv6 ipv6 = (IPv6) eth.getPayload();
532
533 if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
534 return null;
535 }
536 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
537
538 IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
539 IpAddress target = null;
540
541 MessageType type;
542 if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
543 type = MessageType.REQUEST;
544 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
545 target = Ip6Address.valueOf(nsol.getTargetAddress());
546 } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
547 type = MessageType.REPLY;
548 } else {
549 return null;
550 }
551
552 return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
553 }
554
555 /**
556 * Provides context information for a particular ARP or NDP message, with
557 * a unified interface to access data regardless of protocol.
558 */
559 private class MessageContext {
560 private Protocol protocol;
561 private MessageType type;
562
563 private IpAddress target;
564 private IpAddress sender;
565
566 private Ethernet eth;
567 private ConnectPoint inPort;
568
569
570 public MessageContext(Ethernet eth, ConnectPoint inPort,
571 Protocol protocol, MessageType type,
572 IpAddress target, IpAddress sender) {
573 this.eth = eth;
574 this.inPort = inPort;
575 this.protocol = protocol;
576 this.type = type;
577 this.target = target;
578 this.sender = sender;
579 }
580
581 public ConnectPoint inPort() {
582 return inPort;
583 }
584
585 public Ethernet packet() {
586 return eth;
587 }
588
589 public Protocol protocol() {
590 return protocol;
591 }
592
593 public MessageType type() {
594 return type;
595 }
596
597 public VlanId vlan() {
598 return VlanId.vlanId(eth.getVlanID());
599 }
600
601 public MacAddress srcMac() {
602 return MacAddress.valueOf(eth.getSourceMACAddress());
603 }
604
605 public IpAddress target() {
606 return target;
607 }
608
609 public IpAddress sender() {
610 return sender;
611 }
612 }
alshabibb5522ff2014-09-29 19:20:00 -0700613}