blob: ff7ec6b2b104723bf69295a65cfd3a46bf9b1795 [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
Himal Kumarb43724d2016-04-29 14:15:57 +100018import org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.packet.DefaultOutboundPacket;
33import org.onosproject.net.packet.InboundPacket;
34import org.onosproject.net.packet.PacketContext;
35import org.onosproject.net.packet.PacketProcessor;
36import org.onosproject.net.packet.PacketService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070037import org.osgi.service.component.annotations.Activate;
38import org.osgi.service.component.annotations.Component;
39import org.osgi.service.component.annotations.Deactivate;
40import org.osgi.service.component.annotations.Reference;
41import org.osgi.service.component.annotations.ReferenceCardinality;
Himal Kumarb43724d2016-04-29 14:15:57 +100042import org.slf4j.Logger;
43
44import java.nio.ByteBuffer;
45import java.util.Optional;
46import java.util.Set;
47
48import static org.onlab.packet.Ethernet.TYPE_ARP;
49import static org.onosproject.net.packet.PacketPriority.CONTROL;
50import static org.slf4j.LoggerFactory.getLogger;
51
52/**
53 * Component for managing the ARPs.
54 */
55
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056@Component(immediate = true, service = ArpService.class)
Himal Kumarb43724d2016-04-29 14:15:57 +100057public class CastorArpManager implements ArpService {
58
Ray Milkeyd84f89b2018-08-17 14:54:17 -070059 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100060 protected ConnectivityManagerService connectivityManager;
61
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100063 protected PacketService packetService;
64
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100066 protected CoreService coreService;
67
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Himal Kumarb43724d2016-04-29 14:15:57 +100069 protected CastorStore castorStore;
70
71 private ProxyArpProcessor processor = new ProxyArpProcessor();
72
73 private final Logger log = getLogger(getClass());
74 private static final int FLOW_PRIORITY = 500;
75 private static final MacAddress ARP_SOURCEMAC = MacAddress.valueOf("00:00:00:00:00:01");
76 private static final MacAddress ARP_DEST = MacAddress.valueOf("00:00:00:00:00:00");
77 private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
78 private static final IpAddress ARP_SRC = Ip4Address.valueOf("0.0.0.0");
79
80 private ApplicationId appId;
81 Optional<DeviceId> deviceID = null;
82
83 private enum Protocol {
84 ARP
85 }
86
87 private enum MessageType {
88 REQUEST, REPLY
89 }
90
91 @Activate
92 public void activate() {
93 appId = coreService.getAppId(Castor.CASTOR_APP);
94 packetService.addProcessor(processor, PacketProcessor.director(1));
95 requestPackets();
96 }
97
98 @Deactivate
99 public void deactivate() {
100 withdrawIntercepts();
101 packetService.removeProcessor(processor);
102 processor = null;
103 }
104
105 /**
106 * Used to request the ARP packets.
107 */
108 private void requestPackets() {
109 TrafficSelector.Builder selectorBuilder =
110 DefaultTrafficSelector.builder();
111 selectorBuilder.matchEthType(TYPE_ARP);
112 packetService.requestPackets(selectorBuilder.build(), CONTROL, appId);
113 }
114
115 /**
116 * Withdraws the requested ARP packets.
117 */
118 private void withdrawIntercepts() {
Ray Milkey11ce9302019-02-07 14:41:17 -0800119 if (deviceID != null && deviceID.isPresent()) {
120 TrafficSelector.Builder selectorBuilder =
Himal Kumarb43724d2016-04-29 14:15:57 +1000121 DefaultTrafficSelector.builder();
Ray Milkey11ce9302019-02-07 14:41:17 -0800122 selectorBuilder.matchEthType(TYPE_ARP);
123 packetService.cancelPackets(selectorBuilder.build(), CONTROL, appId, deviceID);
124 }
Himal Kumarb43724d2016-04-29 14:15:57 +1000125 }
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}