blob: f1d336fabce1f383edbb43ddd8f62de5c4b0c719 [file] [log] [blame]
Himal Kumarb43724d2016-04-29 14:15:57 +10001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Himal Kumarb43724d2016-04-29 14:15:57 +10003 *
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.castor;
17
18import 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;
24import org.onlab.packet.ARP;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.Ip4Address;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.packet.DefaultOutboundPacket;
39import org.onosproject.net.packet.InboundPacket;
40import org.onosproject.net.packet.PacketContext;
41import org.onosproject.net.packet.PacketProcessor;
42import org.onosproject.net.packet.PacketService;
43import org.slf4j.Logger;
44
45import java.nio.ByteBuffer;
46import java.util.Optional;
47import java.util.Set;
48
49import static org.onlab.packet.Ethernet.TYPE_ARP;
50import static org.onosproject.net.packet.PacketPriority.CONTROL;
51import static org.slf4j.LoggerFactory.getLogger;
52
53/**
54 * Component for managing the ARPs.
55 */
56
57@Component(immediate = true, enabled = true)
58@Service
59public class CastorArpManager implements ArpService {
60
61 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
62 protected ConnectivityManagerService connectivityManager;
63
64 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 protected PacketService packetService;
66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected CoreService coreService;
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected CastorStore castorStore;
72
73 private ProxyArpProcessor processor = new ProxyArpProcessor();
74
75 private final Logger log = getLogger(getClass());
76 private static final int FLOW_PRIORITY = 500;
77 private static final MacAddress ARP_SOURCEMAC = MacAddress.valueOf("00:00:00:00:00:01");
78 private static final MacAddress ARP_DEST = MacAddress.valueOf("00:00:00:00:00:00");
79 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
80 private static final IpAddress ARP_SRC = Ip4Address.valueOf("0.0.0.0");
81
82 private ApplicationId appId;
83 Optional<DeviceId> deviceID = null;
84
85 private enum Protocol {
86 ARP
87 }
88
89 private enum MessageType {
90 REQUEST, REPLY
91 }
92
93 @Activate
94 public void activate() {
95 appId = coreService.getAppId(Castor.CASTOR_APP);
96 packetService.addProcessor(processor, PacketProcessor.director(1));
97 requestPackets();
98 }
99
100 @Deactivate
101 public void deactivate() {
102 withdrawIntercepts();
103 packetService.removeProcessor(processor);
104 processor = null;
105 }
106
107 /**
108 * Used to request the ARP packets.
109 */
110 private void requestPackets() {
111 TrafficSelector.Builder selectorBuilder =
112 DefaultTrafficSelector.builder();
113 selectorBuilder.matchEthType(TYPE_ARP);
114 packetService.requestPackets(selectorBuilder.build(), CONTROL, appId);
115 }
116
117 /**
118 * Withdraws the requested ARP packets.
119 */
120 private void withdrawIntercepts() {
121 TrafficSelector.Builder selectorBuilder =
122 DefaultTrafficSelector.builder();
123 selectorBuilder.matchEthType(TYPE_ARP);
124 packetService.cancelPackets(selectorBuilder.build(), CONTROL, appId, deviceID);
125 }
126
127 /**
128 * Forwards the ARP packet to the specified connect point via packet out.
129 *
130 * @param context The packet context
131 */
132 private void forward(MessageContext context) {
133
134 TrafficTreatment.Builder builder = null;
135 Ethernet eth = context.packet();
136 ByteBuffer buf = ByteBuffer.wrap(eth.serialize());
137
138 IpAddress target = context.target();
139 String value = getMatchingConnectPoint(target);
140 if (value != null) {
141 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(value);
142 builder = DefaultTrafficTreatment.builder();
143 builder.setOutput(connectPoint.port());
144 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
145 }
146 }
147
148 @Override
149 public void createArp(Peer peer) {
150
151 Ethernet packet = null;
152 packet = buildArpRequest(peer);
153 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
154 ConnectPoint connectPoint = ConnectPoint.deviceConnectPoint(peer.getPort());
155
156 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
157 builder.setOutput(connectPoint.port());
158 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(), builder.build(), buf));
159
160 }
161
162 /**
163 * Builds the ARP request when MAC is not known.
164 *
165 * @param peer The Peer whose MAC is not known.
166 * @return Ethernet
167 */
168 private Ethernet buildArpRequest(Peer peer) {
169 ARP arp = new ARP();
170 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
171 .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
172 .setProtocolType(ARP.PROTO_TYPE_IP)
173 .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
174 .setOpCode(ARP.OP_REQUEST);
175
176 arp.setSenderHardwareAddress(ARP_SOURCEMAC.toBytes())
177 .setSenderProtocolAddress(ARP_SRC.toOctets())
178 .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
179 .setTargetProtocolAddress(IpAddress.valueOf(peer.getIpAddress()).toOctets());
180
181 Ethernet ethernet = new Ethernet();
182 ethernet.setEtherType(Ethernet.TYPE_ARP)
183 .setDestinationMACAddress(MacAddress.BROADCAST)
184 .setSourceMACAddress(ARP_SOURCEMAC)
185 .setPayload(arp);
186 ethernet.setPad(true);
187
188 return ethernet;
189 }
190
191 /**
192 * Gets the matching connect point corresponding to the peering IP address.
193 *
194 * @param target Target IP address
195 * @return Connect point as a String
196 */
197 private String getMatchingConnectPoint(IpAddress target) {
198 Set<Peer> peers = castorStore.getAllPeers();
199 for (Peer peer : peers) {
200 IpAddress match = IpAddress.valueOf(peer.getIpAddress());
201 if (match.equals(target)) {
202 return peer.getPort();
203 }
204 }
205 return null;
206 }
207
208 /**
209 * Returns the matching Peer or route server on a Connect Point.
210 *
211 * @param connectPoint The peering connect point.
212 * @return Peer or Route Server
213 */
214 private Peer getMatchingPeer(ConnectPoint connectPoint) {
215
216 for (Peer peer : castorStore.getAllPeers()) {
217 if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
218 return peer;
219 }
220 }
221 return null;
222 }
223
224 /**
225 * Returns matching BGP Peer on a connect point.
226 *
227 * @param connectPoint The peering connect point.
228 * @return The Peer
229 */
230 private Peer getMatchingCustomer(ConnectPoint connectPoint) {
231
232 for (Peer peer : castorStore.getCustomers()) {
233 if (connectPoint.equals(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
234 return peer;
235 }
236 }
237 return null;
238 }
239
240 /**
241 * Updates the IP address to mac address map.
242 *
243 * @param context The message context.
244 */
245 private void updateMac(MessageContext context) {
246
247 if ((castorStore.getAddressMap()).containsKey(context.sender())) {
248 return;
249 }
250 Ethernet eth = context.packet();
251 MacAddress macAddress = eth.getSourceMAC();
252 IpAddress ipAddress = context.sender();
253 castorStore.setAddressMap(ipAddress, macAddress);
254 }
255
256 /**
257 * Setup the layer two flows if not already installed after an ARP packet is received.
258 * If the layer 2 status is true, means layer two flows are already provisioned.
259 * If the status was false, layer 2 flows will be installed at this point. This
260 * happens when the mac address of a peer was not known at the time of its addition.
261 *
262 * @param msgContext The message context.
263 */
264 private void handleArpForL2(MessageContext msgContext) {
265
266 ConnectPoint cp = msgContext.inPort();
267 Peer peer = getMatchingCustomer(cp);
268
269 if (peer != null && !peer.getl2Status()) {
270 connectivityManager.setUpL2(peer);
271 }
272 }
273
274 @Override
275 public boolean handlePacket(PacketContext context) {
276
277 InboundPacket pkt = context.inPacket();
278 Ethernet ethPkt = pkt.parsed();
279
280 if (ethPkt == null) {
281 return false;
282 }
283
284 MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
285
286 if (msgContext == null) {
287 return false;
288 }
289 switch (msgContext.type()) {
290 case REPLY:
291 forward(msgContext);
292 updateMac(msgContext);
293 handleArpForL2(msgContext);
294 break;
295 case REQUEST:
296 forward(msgContext);
297 updateMac(msgContext);
298 handleArpForL2(msgContext);
299 break;
300 default:
301 return false;
302 }
303 context.block();
304 return true;
305 }
306
307 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
308 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
309 return createArpContext(eth, inPort);
310 }
311 return null;
312 }
313
314 /**
315 * Extracts context information from ARP packets.
316 *
317 * @param eth input Ethernet frame that is thought to be ARP
318 * @param inPort in port
319 * @return MessageContext object if the packet was a valid ARP packet,
320 * otherwise null
321 */
322 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
323 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
324 return null;
325 }
326
327 ARP arp = (ARP) eth.getPayload();
328
329 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
330 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
331
332 MessageType type;
333 if (arp.getOpCode() == ARP.OP_REQUEST) {
334 type = MessageType.REQUEST;
335 } else if (arp.getOpCode() == ARP.OP_REPLY) {
336 type = MessageType.REPLY;
337 } else {
338 return null;
339 }
340 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
341 }
342
343 private class MessageContext {
344 private Protocol protocol;
345 private MessageType type;
346
347 private IpAddress target;
348 private IpAddress sender;
349
350 private Ethernet eth;
351 private ConnectPoint inPort;
352
353 public MessageContext(Ethernet eth, ConnectPoint inPort,
354 Protocol protocol, MessageType type,
355 IpAddress target, IpAddress sender) {
356 this.eth = eth;
357 this.inPort = inPort;
358 this.protocol = protocol;
359 this.type = type;
360 this.target = target;
361 this.sender = sender;
362 }
363
364 public ConnectPoint inPort() {
365 return inPort;
366 }
367
368 public Ethernet packet() {
369 return eth;
370 }
371
372 public Protocol protocol() {
373 return protocol;
374 }
375
376 public MessageType type() {
377 return type;
378 }
379
380 public VlanId vlan() {
381 return VlanId.vlanId(eth.getVlanID());
382 }
383
384 public MacAddress srcMac() {
385 return MacAddress.valueOf(eth.getSourceMACAddress());
386 }
387
388 public IpAddress target() {
389 return target;
390 }
391
392 public IpAddress sender() {
393 return sender;
394 }
395 }
396 private class ProxyArpProcessor implements PacketProcessor {
397
398 @Override
399 public void process(PacketContext context) {
400
401 if (context.isHandled()) {
402 return;
403 }
404 InboundPacket pkt = context.inPacket();
405 Ethernet ethPkt = pkt.parsed();
406 if (ethPkt == null) {
407 return;
408 }
409 if (ethPkt.getEtherType() == TYPE_ARP) {
410 //handle the arp packet.
411 handlePacket(context);
412 } else {
413 return;
414 }
415 }
416 }
417}