blob: 9b554e348eabc87eacc83ed80950c7bd0c277c91 [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sanghob35a6192015-04-01 13:05:26 -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 */
16package org.onosproject.segmentrouting;
17
18import org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
sanghob35a6192015-04-01 13:05:26 -070022import org.onlab.packet.MacAddress;
Charles Chan68aa62d2015-11-09 16:37:23 -080023import org.onlab.packet.VlanId;
sanghob35a6192015-04-01 13:05:26 -070024import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.Host;
sanghob35a6192015-04-01 13:05:26 -070027import org.onosproject.net.flow.DefaultTrafficTreatment;
28import org.onosproject.net.flow.TrafficTreatment;
29import org.onosproject.net.packet.DefaultOutboundPacket;
30import org.onosproject.net.packet.InboundPacket;
31import org.onosproject.net.HostId;
32import org.onosproject.net.packet.OutboundPacket;
Charles Chan0b4e6182015-11-03 10:42:14 -080033import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
34import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070035import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38import java.nio.ByteBuffer;
Saurav Das837e0bb2015-10-30 17:45:38 -070039import java.util.Set;
40
sanghob35a6192015-04-01 13:05:26 -070041import static com.google.common.base.Preconditions.checkNotNull;
42
Charles Chane849c192016-01-11 18:28:54 -080043/**
44 * Handler of ARP packets that responses or forwards ARP packets that
45 * are sent to the controller.
46 */
sanghob35a6192015-04-01 13:05:26 -070047public class ArpHandler {
48
49 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
50
51 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070052 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070053
54 /**
55 * Creates an ArpHandler object.
56 *
57 * @param srManager SegmentRoutingManager object
58 */
59 public ArpHandler(SegmentRoutingManager srManager) {
60 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070061 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070062 }
63
64 /**
65 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080066 *
sanghob35a6192015-04-01 13:05:26 -070067 * If it is an ARP request to router itself or known hosts,
68 * then it sends ARP response.
69 * If it is an ARP request to unknown hosts in its own subnet,
70 * then it flood the ARP request to the ports.
71 * If it is an ARP response, then set a flow rule for the host
72 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080073 * <p>
74 * Note: We handles all ARP packet in, even for those ARP packets between
75 * hosts in the same subnet.
76 * For an ARP packet with broadcast destination MAC,
77 * some switches pipelines will send it to the controller due to table miss,
Saurav Das49831642016-02-05 13:15:20 -080078 * other switches will flood the packets directly in the data plane without
Charles Chan68aa62d2015-11-09 16:37:23 -080079 * packet in.
80 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070081 *
82 * @param pkt incoming packet
83 */
84 public void processPacketIn(InboundPacket pkt) {
85
86 Ethernet ethernet = pkt.parsed();
87 ARP arp = (ARP) ethernet.getPayload();
88
89 ConnectPoint connectPoint = pkt.receivedFrom();
sanghob35a6192015-04-01 13:05:26 -070090 DeviceId deviceId = connectPoint.deviceId();
sanghob35a6192015-04-01 13:05:26 -070091 if (arp.getOpCode() == ARP.OP_REQUEST) {
92 handleArpRequest(deviceId, connectPoint, ethernet);
93 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -080094 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -070095 }
96 }
97
98 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -070099 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800100 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700101 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800102 arpRequest.getTargetHardwareAddress()),
103 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700104
Charles Chan68aa62d2015-11-09 16:37:23 -0800105 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800106 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700107 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800108 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700109 } else {
110 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700112 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 removeVlanAndForward(payload, targetHost.location());
114 // ARP request for unknown host in the subnet. Flood in the subnet.
115 } else {
116 removeVlanAndFlood(payload, inPort);
117 }
118 }
119 }
sanghob35a6192015-04-01 13:05:26 -0700120
Charles Chan68aa62d2015-11-09 16:37:23 -0800121 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
122 ARP arpReply = (ARP) payload.getPayload();
123 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
124 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
125 arpReply.getTargetHardwareAddress()),
126 vlanId);
127
128 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800129 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800130 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
131 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
132 } else {
133 Host targetHost = srManager.hostService.getHost(targetHostId);
134 // ARP reply for known hosts. Forward to the host.
135 if (targetHost != null) {
136 removeVlanAndForward(payload, targetHost.location());
137 // ARP reply for unknown host, Flood in the subnet.
138 } else {
139 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800140 if (vlanId.equals(
141 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800142 return;
143 }
144 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700145 }
sanghob35a6192015-04-01 13:05:26 -0700146 }
147 }
148
149
Saurav Das4ce45962015-11-24 23:21:05 -0800150 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
151 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
152 arpMsg.getTargetProtocolAddress());
153 Set<Ip4Address> gatewayIpAddresses = null;
154 try {
155 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700156 return true;
157 }
Saurav Das4ce45962015-11-24 23:21:05 -0800158 gatewayIpAddresses = config.getPortIPs(deviceId);
159 } catch (DeviceConfigNotFoundException e) {
160 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
161 }
162 if (gatewayIpAddresses != null &&
163 gatewayIpAddresses.contains(targetProtocolAddress)) {
164 return true;
sanghob35a6192015-04-01 13:05:26 -0700165 }
166 return false;
167 }
168
sanghob35a6192015-04-01 13:05:26 -0700169 /**
170 * Sends an APR request for the target IP address to all ports except in-port.
171 *
172 * @param deviceId Switch device ID
173 * @param targetAddress target IP address for ARP
174 * @param inPort in-port
175 */
176 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800177 byte[] senderMacAddress;
178 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700179
Charles Chan0b4e6182015-11-03 10:42:14 -0800180 try {
181 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
182 senderIpAddress = config.getRouterIp(deviceId).toOctets();
183 } catch (DeviceConfigNotFoundException e) {
184 log.warn(e.getMessage() + " Aborting sendArpRequest.");
185 return;
186 }
sanghob35a6192015-04-01 13:05:26 -0700187
188 ARP arpRequest = new ARP();
189 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
190 .setProtocolType(ARP.PROTO_TYPE_IP)
191 .setHardwareAddressLength(
192 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
193 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
194 .setOpCode(ARP.OP_REQUEST)
195 .setSenderHardwareAddress(senderMacAddress)
196 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
197 .setSenderProtocolAddress(senderIpAddress)
198 .setTargetProtocolAddress(targetAddress.toOctets());
199
200 Ethernet eth = new Ethernet();
201 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
202 .setSourceMACAddress(senderMacAddress)
203 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
204
Charles Chan68aa62d2015-11-09 16:37:23 -0800205 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700206 }
207
Charles Chan68aa62d2015-11-09 16:37:23 -0800208 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700209 ARP arpReply = new ARP();
210 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
211 .setProtocolType(ARP.PROTO_TYPE_IP)
212 .setHardwareAddressLength(
213 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
214 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
215 .setOpCode(ARP.OP_REPLY)
216 .setSenderHardwareAddress(targetMac.toBytes())
217 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
218 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
219 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
220
221 Ethernet eth = new Ethernet();
222 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
223 .setSourceMACAddress(targetMac.toBytes())
224 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
225
Flavio Castro6816f252016-01-14 14:53:33 -0800226 MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress());
227 HostId dstId = HostId.hostId(hostMac, vlanId);
sanghob35a6192015-04-01 13:05:26 -0700228 Host dst = srManager.hostService.getHost(dstId);
229 if (dst == null) {
Flavio Castro6816f252016-01-14 14:53:33 -0800230 log.warn("Cannot send ARP response to host {}", dstId);
sanghob35a6192015-04-01 13:05:26 -0700231 return;
232 }
233
234 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
235 setOutput(dst.location().port()).build();
236 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
237 treatment, ByteBuffer.wrap(eth.serialize()));
238
239 srManager.packetService.emit(packet);
240 }
241
Charles Chan68aa62d2015-11-09 16:37:23 -0800242 /**
243 * Remove VLAN tag and flood to all ports in the same subnet.
244 *
245 * @param packet packet to be flooded
246 * @param inPort where the packet comes from
247 */
248 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
249 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
250 ((ARP) packet.getPayload()).getTargetProtocolAddress()
251 );
sanghob35a6192015-04-01 13:05:26 -0700252
Saurav Das7a1ffca2016-03-28 19:00:18 -0700253 try {
254 srManager.deviceConfiguration
255 .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
256 if (subnet.contains(targetProtocolAddress)) {
257 ports.stream()
258 .filter(port -> port != inPort.port())
259 .forEach(port -> {
260 removeVlanAndForward(packet,
261 new ConnectPoint(inPort.deviceId(), port));
262 });
263 }
264 });
265 } catch (DeviceConfigNotFoundException e) {
266 log.warn(e.getMessage()
267 + " Cannot flood in subnet as device config not available"
268 + " for device: " + inPort.deviceId());
269 }
sanghob35a6192015-04-01 13:05:26 -0700270 }
271
Charles Chan68aa62d2015-11-09 16:37:23 -0800272 /**
273 * Remove VLAN tag and packet out to given port.
274 *
275 * Note: In current implementation, we expect all communication with
276 * end hosts within a subnet to be untagged.
277 * <p>
278 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
279 * removed before egress.
280 * <p>
281 * For those pipelines that do not assign internal VLAN, the packet remains
282 * untagged.
283 *
284 * @param packet packet to be forwarded
285 * @param outPort where the packet should be forwarded
286 */
287 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
288 packet.setEtherType(Ethernet.TYPE_ARP);
289 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
290 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
291
292 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
293 tbuilder.setOutput(outPort.port());
294 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
295 tbuilder.build(), buf));
296 }
sanghob35a6192015-04-01 13:05:26 -0700297}