blob: 513248932bf7207c88bc5ecaf70dad95c5d3cecb [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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.PortNumber;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.packet.DefaultOutboundPacket;
31import org.onosproject.net.packet.InboundPacket;
32import org.onosproject.net.HostId;
33import org.onosproject.net.packet.OutboundPacket;
Charles Chan0b4e6182015-11-03 10:42:14 -080034import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
35import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.nio.ByteBuffer;
Saurav Das837e0bb2015-10-30 17:45:38 -070040import java.util.Set;
41
sanghob35a6192015-04-01 13:05:26 -070042import static com.google.common.base.Preconditions.checkNotNull;
43
Charles Chane849c192016-01-11 18:28:54 -080044/**
45 * Handler of ARP packets that responses or forwards ARP packets that
46 * are sent to the controller.
47 */
sanghob35a6192015-04-01 13:05:26 -070048public class ArpHandler {
49
50 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
51
52 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070053 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070054
55 /**
56 * Creates an ArpHandler object.
57 *
58 * @param srManager SegmentRoutingManager object
59 */
60 public ArpHandler(SegmentRoutingManager srManager) {
61 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070062 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070063 }
64
65 /**
66 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080067 *
sanghob35a6192015-04-01 13:05:26 -070068 * If it is an ARP request to router itself or known hosts,
69 * then it sends ARP response.
70 * If it is an ARP request to unknown hosts in its own subnet,
71 * then it flood the ARP request to the ports.
72 * If it is an ARP response, then set a flow rule for the host
73 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080074 * <p>
75 * Note: We handles all ARP packet in, even for those ARP packets between
76 * hosts in the same subnet.
77 * For an ARP packet with broadcast destination MAC,
78 * some switches pipelines will send it to the controller due to table miss,
79 * other swithches will flood the packets directly in the data plane without
80 * packet in.
81 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070082 *
83 * @param pkt incoming packet
84 */
85 public void processPacketIn(InboundPacket pkt) {
86
87 Ethernet ethernet = pkt.parsed();
88 ARP arp = (ARP) ethernet.getPayload();
89
90 ConnectPoint connectPoint = pkt.receivedFrom();
91 PortNumber inPort = connectPoint.port();
92 DeviceId deviceId = connectPoint.deviceId();
93 byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
94 Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
95
96 srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
97 valueOf(senderMacAddressByte), inPort);
98
99 if (arp.getOpCode() == ARP.OP_REQUEST) {
100 handleArpRequest(deviceId, connectPoint, ethernet);
101 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -0800102 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -0700103 }
104 }
105
106 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -0700107 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800108 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700109 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800110 arpRequest.getTargetHardwareAddress()),
111 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700112
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800114 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700115 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800116 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700117 } else {
118 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800119 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700120 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800121 removeVlanAndForward(payload, targetHost.location());
122 // ARP request for unknown host in the subnet. Flood in the subnet.
123 } else {
124 removeVlanAndFlood(payload, inPort);
125 }
126 }
127 }
sanghob35a6192015-04-01 13:05:26 -0700128
Charles Chan68aa62d2015-11-09 16:37:23 -0800129 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
130 ARP arpReply = (ARP) payload.getPayload();
131 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
132 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
133 arpReply.getTargetHardwareAddress()),
134 vlanId);
135
136 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800137 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800138 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
139 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
140 } else {
141 Host targetHost = srManager.hostService.getHost(targetHostId);
142 // ARP reply for known hosts. Forward to the host.
143 if (targetHost != null) {
144 removeVlanAndForward(payload, targetHost.location());
145 // ARP reply for unknown host, Flood in the subnet.
146 } else {
147 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800148 if (vlanId.equals(
149 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800150 return;
151 }
152 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700153 }
sanghob35a6192015-04-01 13:05:26 -0700154 }
155 }
156
157
Saurav Das4ce45962015-11-24 23:21:05 -0800158 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
159 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
160 arpMsg.getTargetProtocolAddress());
161 Set<Ip4Address> gatewayIpAddresses = null;
162 try {
163 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700164 return true;
165 }
Saurav Das4ce45962015-11-24 23:21:05 -0800166 gatewayIpAddresses = config.getPortIPs(deviceId);
167 } catch (DeviceConfigNotFoundException e) {
168 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
169 }
170 if (gatewayIpAddresses != null &&
171 gatewayIpAddresses.contains(targetProtocolAddress)) {
172 return true;
sanghob35a6192015-04-01 13:05:26 -0700173 }
174 return false;
175 }
176
sanghob35a6192015-04-01 13:05:26 -0700177 /**
178 * Sends an APR request for the target IP address to all ports except in-port.
179 *
180 * @param deviceId Switch device ID
181 * @param targetAddress target IP address for ARP
182 * @param inPort in-port
183 */
184 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800185 byte[] senderMacAddress;
186 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700187
Charles Chan0b4e6182015-11-03 10:42:14 -0800188 try {
189 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
190 senderIpAddress = config.getRouterIp(deviceId).toOctets();
191 } catch (DeviceConfigNotFoundException e) {
192 log.warn(e.getMessage() + " Aborting sendArpRequest.");
193 return;
194 }
sanghob35a6192015-04-01 13:05:26 -0700195
196 ARP arpRequest = new ARP();
197 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
198 .setProtocolType(ARP.PROTO_TYPE_IP)
199 .setHardwareAddressLength(
200 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
201 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
202 .setOpCode(ARP.OP_REQUEST)
203 .setSenderHardwareAddress(senderMacAddress)
204 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
205 .setSenderProtocolAddress(senderIpAddress)
206 .setTargetProtocolAddress(targetAddress.toOctets());
207
208 Ethernet eth = new Ethernet();
209 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
210 .setSourceMACAddress(senderMacAddress)
211 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
212
Charles Chan68aa62d2015-11-09 16:37:23 -0800213 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700214 }
215
Charles Chan68aa62d2015-11-09 16:37:23 -0800216 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700217 ARP arpReply = new ARP();
218 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
219 .setProtocolType(ARP.PROTO_TYPE_IP)
220 .setHardwareAddressLength(
221 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
222 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
223 .setOpCode(ARP.OP_REPLY)
224 .setSenderHardwareAddress(targetMac.toBytes())
225 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
226 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
227 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
228
229 Ethernet eth = new Ethernet();
230 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
231 .setSourceMACAddress(targetMac.toBytes())
232 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
233
Flavio Castro6816f252016-01-14 14:53:33 -0800234 MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress());
235 HostId dstId = HostId.hostId(hostMac, vlanId);
sanghob35a6192015-04-01 13:05:26 -0700236 Host dst = srManager.hostService.getHost(dstId);
237 if (dst == null) {
Flavio Castro6816f252016-01-14 14:53:33 -0800238 log.warn("Cannot send ARP response to host {}", dstId);
sanghob35a6192015-04-01 13:05:26 -0700239 return;
240 }
241
242 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
243 setOutput(dst.location().port()).build();
244 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
245 treatment, ByteBuffer.wrap(eth.serialize()));
246
247 srManager.packetService.emit(packet);
248 }
249
Charles Chan68aa62d2015-11-09 16:37:23 -0800250 /**
251 * Remove VLAN tag and flood to all ports in the same subnet.
252 *
253 * @param packet packet to be flooded
254 * @param inPort where the packet comes from
255 */
256 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
257 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
258 ((ARP) packet.getPayload()).getTargetProtocolAddress()
259 );
sanghob35a6192015-04-01 13:05:26 -0700260
Charles Chan68aa62d2015-11-09 16:37:23 -0800261 srManager.deviceConfiguration.getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
262 if (subnet.contains(targetProtocolAddress)) {
263 ports.stream()
264 .filter(port -> port != inPort.port())
265 .forEach(port -> {
266 removeVlanAndForward(packet, new ConnectPoint(inPort.deviceId(), port));
267 });
sanghob35a6192015-04-01 13:05:26 -0700268 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800269 });
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}