blob: 7f4bcb15da49e1fbb6989007b8fe7e9abb1b572c [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
44public class ArpHandler {
45
46 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
47
48 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070049 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070050
51 /**
52 * Creates an ArpHandler object.
53 *
54 * @param srManager SegmentRoutingManager object
55 */
56 public ArpHandler(SegmentRoutingManager srManager) {
57 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070058 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070059 }
60
61 /**
62 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080063 *
sanghob35a6192015-04-01 13:05:26 -070064 * If it is an ARP request to router itself or known hosts,
65 * then it sends ARP response.
66 * If it is an ARP request to unknown hosts in its own subnet,
67 * then it flood the ARP request to the ports.
68 * If it is an ARP response, then set a flow rule for the host
69 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080070 * <p>
71 * Note: We handles all ARP packet in, even for those ARP packets between
72 * hosts in the same subnet.
73 * For an ARP packet with broadcast destination MAC,
74 * some switches pipelines will send it to the controller due to table miss,
75 * other swithches will flood the packets directly in the data plane without
76 * packet in.
77 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070078 *
79 * @param pkt incoming packet
80 */
81 public void processPacketIn(InboundPacket pkt) {
82
83 Ethernet ethernet = pkt.parsed();
84 ARP arp = (ARP) ethernet.getPayload();
85
86 ConnectPoint connectPoint = pkt.receivedFrom();
87 PortNumber inPort = connectPoint.port();
88 DeviceId deviceId = connectPoint.deviceId();
89 byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
90 Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
91
92 srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
93 valueOf(senderMacAddressByte), inPort);
94
95 if (arp.getOpCode() == ARP.OP_REQUEST) {
96 handleArpRequest(deviceId, connectPoint, ethernet);
97 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -080098 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -070099 }
100 }
101
102 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -0700103 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800104 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700105 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800106 arpRequest.getTargetHardwareAddress()),
107 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700108
Charles Chan68aa62d2015-11-09 16:37:23 -0800109 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800110 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700111 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800112 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700113 } else {
114 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800115 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700116 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800117 removeVlanAndForward(payload, targetHost.location());
118 // ARP request for unknown host in the subnet. Flood in the subnet.
119 } else {
120 removeVlanAndFlood(payload, inPort);
121 }
122 }
123 }
sanghob35a6192015-04-01 13:05:26 -0700124
Charles Chan68aa62d2015-11-09 16:37:23 -0800125 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
126 ARP arpReply = (ARP) payload.getPayload();
127 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
128 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
129 arpReply.getTargetHardwareAddress()),
130 vlanId);
131
132 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800133 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800134 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
135 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
136 } else {
137 Host targetHost = srManager.hostService.getHost(targetHostId);
138 // ARP reply for known hosts. Forward to the host.
139 if (targetHost != null) {
140 removeVlanAndForward(payload, targetHost.location());
141 // ARP reply for unknown host, Flood in the subnet.
142 } else {
143 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800144 if (vlanId.equals(
145 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800146 return;
147 }
148 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700149 }
sanghob35a6192015-04-01 13:05:26 -0700150 }
151 }
152
153
Saurav Das4ce45962015-11-24 23:21:05 -0800154 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
155 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
156 arpMsg.getTargetProtocolAddress());
157 Set<Ip4Address> gatewayIpAddresses = null;
158 try {
159 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700160 return true;
161 }
Saurav Das4ce45962015-11-24 23:21:05 -0800162 gatewayIpAddresses = config.getPortIPs(deviceId);
163 } catch (DeviceConfigNotFoundException e) {
164 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
165 }
166 if (gatewayIpAddresses != null &&
167 gatewayIpAddresses.contains(targetProtocolAddress)) {
168 return true;
sanghob35a6192015-04-01 13:05:26 -0700169 }
170 return false;
171 }
172
sanghob35a6192015-04-01 13:05:26 -0700173 /**
174 * Sends an APR request for the target IP address to all ports except in-port.
175 *
176 * @param deviceId Switch device ID
177 * @param targetAddress target IP address for ARP
178 * @param inPort in-port
179 */
180 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800181 byte[] senderMacAddress;
182 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700183
Charles Chan0b4e6182015-11-03 10:42:14 -0800184 try {
185 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
186 senderIpAddress = config.getRouterIp(deviceId).toOctets();
187 } catch (DeviceConfigNotFoundException e) {
188 log.warn(e.getMessage() + " Aborting sendArpRequest.");
189 return;
190 }
sanghob35a6192015-04-01 13:05:26 -0700191
192 ARP arpRequest = new ARP();
193 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
194 .setProtocolType(ARP.PROTO_TYPE_IP)
195 .setHardwareAddressLength(
196 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
197 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
198 .setOpCode(ARP.OP_REQUEST)
199 .setSenderHardwareAddress(senderMacAddress)
200 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
201 .setSenderProtocolAddress(senderIpAddress)
202 .setTargetProtocolAddress(targetAddress.toOctets());
203
204 Ethernet eth = new Ethernet();
205 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
206 .setSourceMACAddress(senderMacAddress)
207 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
208
Charles Chan68aa62d2015-11-09 16:37:23 -0800209 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700210 }
211
Charles Chan68aa62d2015-11-09 16:37:23 -0800212 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700213 ARP arpReply = new ARP();
214 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
215 .setProtocolType(ARP.PROTO_TYPE_IP)
216 .setHardwareAddressLength(
217 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
218 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
219 .setOpCode(ARP.OP_REPLY)
220 .setSenderHardwareAddress(targetMac.toBytes())
221 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
222 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
223 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
224
225 Ethernet eth = new Ethernet();
226 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
227 .setSourceMACAddress(targetMac.toBytes())
228 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
229
230
Charles Chan68aa62d2015-11-09 16:37:23 -0800231 HostId dstId = HostId.hostId(
232 MacAddress.valueOf(arpReply.getTargetHardwareAddress()),
233 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700234 Host dst = srManager.hostService.getHost(dstId);
235 if (dst == null) {
236 log.warn("Cannot send ARP response to unknown device");
237 return;
238 }
239
240 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
241 setOutput(dst.location().port()).build();
242 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
243 treatment, ByteBuffer.wrap(eth.serialize()));
244
245 srManager.packetService.emit(packet);
246 }
247
Charles Chan68aa62d2015-11-09 16:37:23 -0800248 /**
249 * Remove VLAN tag and flood to all ports in the same subnet.
250 *
251 * @param packet packet to be flooded
252 * @param inPort where the packet comes from
253 */
254 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
255 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
256 ((ARP) packet.getPayload()).getTargetProtocolAddress()
257 );
sanghob35a6192015-04-01 13:05:26 -0700258
Charles Chan68aa62d2015-11-09 16:37:23 -0800259 srManager.deviceConfiguration.getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
260 if (subnet.contains(targetProtocolAddress)) {
261 ports.stream()
262 .filter(port -> port != inPort.port())
263 .forEach(port -> {
264 removeVlanAndForward(packet, new ConnectPoint(inPort.deviceId(), port));
265 });
sanghob35a6192015-04-01 13:05:26 -0700266 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800267 });
sanghob35a6192015-04-01 13:05:26 -0700268 }
269
Charles Chan68aa62d2015-11-09 16:37:23 -0800270 /**
271 * Remove VLAN tag and packet out to given port.
272 *
273 * Note: In current implementation, we expect all communication with
274 * end hosts within a subnet to be untagged.
275 * <p>
276 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
277 * removed before egress.
278 * <p>
279 * For those pipelines that do not assign internal VLAN, the packet remains
280 * untagged.
281 *
282 * @param packet packet to be forwarded
283 * @param outPort where the packet should be forwarded
284 */
285 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
286 packet.setEtherType(Ethernet.TYPE_ARP);
287 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
288 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
289
290 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
291 tbuilder.setOutput(outPort.port());
292 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
293 tbuilder.build(), buf));
294 }
sanghob35a6192015-04-01 13:05:26 -0700295}