blob: b35f4021625ef6f468167c0f98673e010b5c15c1 [file] [log] [blame]
Sangho Shin463bee52014-09-29 15:14:43 -07001
2/*******************************************************************************
3 * Copyright (c) 2014 Open Networking Laboratory.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Apache License v2.0
6 * which accompanies this distribution, and is available at
7 * http://www.apache.org/licenses/LICENSE-2.0
8 ******************************************************************************/
9
Sangho Shin2f263692014-09-15 14:09:41 -070010package net.onrc.onos.apps.segmentrouting;
11
12import java.util.ArrayList;
Sangho Shin463bee52014-09-29 15:14:43 -070013import java.util.LinkedList;
Sangho Shin2f263692014-09-15 14:09:41 -070014import java.util.List;
Sangho Shin463bee52014-09-29 15:14:43 -070015import java.util.Queue;
Sangho Shin2f263692014-09-15 14:09:41 -070016
17import net.floodlightcontroller.core.IFloodlightProviderService;
18import net.floodlightcontroller.core.IOFSwitch;
19import net.floodlightcontroller.core.module.FloodlightModuleContext;
Sangho Shin2f263692014-09-15 14:09:41 -070020import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
21import net.onrc.onos.core.packet.Ethernet;
Sangho Shin79c8d452014-09-18 09:50:21 -070022import net.onrc.onos.core.packet.ICMP;
Sangho Shin2f263692014-09-15 14:09:41 -070023import net.onrc.onos.core.packet.IPv4;
Srikanth Vavilapalli5ddf8f02014-09-24 11:02:28 -070024import net.onrc.onos.core.topology.Host;
Sangho Shin2f263692014-09-15 14:09:41 -070025import net.onrc.onos.core.topology.ITopologyService;
26import net.onrc.onos.core.topology.MutableTopology;
27import net.onrc.onos.core.topology.Port;
28import net.onrc.onos.core.topology.Switch;
Sangho Shin79c8d452014-09-18 09:50:21 -070029import net.onrc.onos.core.util.SwitchPort;
Sangho Shin2f263692014-09-15 14:09:41 -070030
Sangho Shin1aa93542014-09-22 09:49:44 -070031import org.json.JSONArray;
32import org.json.JSONException;
Sangho Shin2f263692014-09-15 14:09:41 -070033import org.projectfloodlight.openflow.protocol.OFFactory;
34import org.projectfloodlight.openflow.protocol.OFMatchV3;
35import org.projectfloodlight.openflow.protocol.OFMessage;
36import org.projectfloodlight.openflow.protocol.OFOxmList;
Sangho Shinf41ac0a2014-09-19 09:50:30 -070037import org.projectfloodlight.openflow.protocol.OFPacketOut;
Sangho Shin2f263692014-09-15 14:09:41 -070038import org.projectfloodlight.openflow.protocol.action.OFAction;
39import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
Sangho Shin1aa93542014-09-22 09:49:44 -070040import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
Sangho Shinf41ac0a2014-09-19 09:50:30 -070041import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel;
42import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
Sangho Shin2f263692014-09-15 14:09:41 -070043import org.projectfloodlight.openflow.types.EthType;
44import org.projectfloodlight.openflow.types.IPv4Address;
Sangho Shin2f263692014-09-15 14:09:41 -070045import org.projectfloodlight.openflow.types.OFBufferId;
46import org.projectfloodlight.openflow.types.OFPort;
Sangho Shinf41ac0a2014-09-19 09:50:30 -070047import org.projectfloodlight.openflow.types.OFVlanVidMatch;
Sangho Shin2f263692014-09-15 14:09:41 -070048import org.projectfloodlight.openflow.types.TableId;
Sangho Shinf41ac0a2014-09-19 09:50:30 -070049import org.projectfloodlight.openflow.types.U32;
Sangho Shin2f263692014-09-15 14:09:41 -070050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
Srikanth Vavilapallib1fce732014-09-24 14:09:50 -070053public class IcmpHandler {
Sangho Shin2f263692014-09-15 14:09:41 -070054
55 private SegmentRoutingManager srManager;
56 private IFloodlightProviderService floodlightProvider;
57 private MutableTopology mutableTopology;
Sangho Shin2f263692014-09-15 14:09:41 -070058 private ITopologyService topologyService;
59 private static final Logger log = LoggerFactory
Sangho Shinf41ac0a2014-09-19 09:50:30 -070060 .getLogger(IcmpHandler.class);
Sangho Shin2f263692014-09-15 14:09:41 -070061
62 private IFlowPusherService flowPusher;
Sangho Shin1aa93542014-09-22 09:49:44 -070063 private boolean controllerPortAllowed = false;
Sangho Shin2f263692014-09-15 14:09:41 -070064
Sangho Shin463bee52014-09-29 15:14:43 -070065 private Queue<IPv4> icmpQueue = new LinkedList<IPv4>();
66
Sangho Shin2f263692014-09-15 14:09:41 -070067 private static final int TABLE_VLAN = 0;
68 private static final int TABLE_TMAC = 1;
69 private static final int TABLE_IPv4_UNICAST = 2;
70 private static final int TABLE_MPLS = 3;
71 private static final int TABLE_META = 4;
72 private static final int TABLE_ACL = 5;
73
74 private static final short MAX_PRIORITY = (short) 0xffff;
75 private static final short SLASH_24_PRIORITY = (short) 0xfff0;
76 private static final short SLASH_16_PRIORITY = (short) 0xff00;
77 private static final short SLASH_8_PRIORITY = (short) 0xf000;
78 private static final short MIN_PRIORITY = 0x0;
79
Sangho Shin1aa93542014-09-22 09:49:44 -070080 private static final int ICMP_TYPE_ECHO = 0x08;
81 private static final int ICMP_TYPE_REPLY = 0x00;
82
Sangho Shin2f263692014-09-15 14:09:41 -070083
84 public IcmpHandler(FloodlightModuleContext context, SegmentRoutingManager manager) {
85
86 this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
87 this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
Sangho Shin2f263692014-09-15 14:09:41 -070088 this.topologyService = context.getServiceImpl(ITopologyService.class);
89 this.mutableTopology = topologyService.getTopology();
90
91 this.srManager = manager;
Sangho Shin2f263692014-09-15 14:09:41 -070092 }
93
Sangho Shin463bee52014-09-29 15:14:43 -070094 /**
95 * handle ICMP packets
96 * If it is for ICMP echo to router IP or any subnet GW IP,
97 * then send ICMP response on behalf of the switch.
98 * If it is for any hosts in subnets of the switches, but if the MAC
99 * address is not known, then send an ARP request to the subent.
100 * If the MAC address is known, then set the routing rule to the switch
101 *
102 * @param sw
103 * @param inPort
104 * @param payload
105 */
Srikanth Vavilapallib1fce732014-09-24 14:09:50 -0700106 public void processPacketIn(Switch sw, Port inPort, Ethernet payload) {
Sangho Shin2f263692014-09-15 14:09:41 -0700107
108 if (payload.getEtherType() == Ethernet.TYPE_IPV4) {
109
110 IPv4 ipv4 = (IPv4)payload.getPayload();
Sangho Shin79c8d452014-09-18 09:50:21 -0700111
Sangho Shin2f263692014-09-15 14:09:41 -0700112 if (ipv4.getProtocol() == IPv4.PROTOCOL_ICMP) {
Srikanth Vavilapalli5ddf8f02014-09-24 11:02:28 -0700113
114 log.debug("ICMPHandler: Received a ICMP packet {} from sw {} ",
115 payload.toString(), sw.getDpid());
Sangho Shin463bee52014-09-29 15:14:43 -0700116 IPv4Address destinationAddress =
117 IPv4Address.of(ipv4.getDestinationAddress());
Sangho Shin2f263692014-09-15 14:09:41 -0700118
Sangho Shin79c8d452014-09-18 09:50:21 -0700119 // Check if it is ICMP request to the switch
120 String switchIpAddressSlash = sw.getStringAttribute("routerIp");
121 if (switchIpAddressSlash != null) {
Sangho Shineb083032014-09-22 16:11:34 -0700122 String switchIpAddressStr
123 = switchIpAddressSlash.substring(0, switchIpAddressSlash.indexOf('/'));
Sangho Shin79c8d452014-09-18 09:50:21 -0700124 IPv4Address switchIpAddress = IPv4Address.of(switchIpAddressStr);
Sangho Shineb083032014-09-22 16:11:34 -0700125 List<String> gatewayIps = getSubnetGatewayIps(sw);
Sangho Shin1aa93542014-09-22 09:49:44 -0700126 if (((ICMP)ipv4.getPayload()).getIcmpType() == ICMP_TYPE_ECHO &&
Sangho Shin463bee52014-09-29 15:14:43 -0700127 (destinationAddress.getInt() == switchIpAddress.getInt() ||
128 gatewayIps.contains(destinationAddress.toString()))) {
Srikanth Vavilapalli5ddf8f02014-09-24 11:02:28 -0700129 log.debug("ICMPHandler: ICMP packet for sw {} and "
130 + "sending ICMP response ", sw.getDpid());
Sangho Shin79c8d452014-09-18 09:50:21 -0700131 sendICMPResponse(sw, inPort, payload);
Sangho Shin463bee52014-09-29 15:14:43 -0700132 srManager.getIpPacketFromQueue(destinationAddress.getBytes());
Sangho Shin79c8d452014-09-18 09:50:21 -0700133 return;
134 }
135 }
Srikanth Vavilapalli5ddf8f02014-09-24 11:02:28 -0700136
137 /* Check if ICMP is for any switch known host */
138 for (Host host: sw.getHosts()) {
139 IPv4Address hostIpAddress =
140 IPv4Address.of(host.getIpAddress());
141 if (hostIpAddress != null &&
142 hostIpAddress.equals(destinationAddress)) {
143 /* TODO: We should not have come here as ARP itself
144 * would have installed a Route to the host. See if
145 * we can remove this code
146 */
147 log.debug("ICMPHandler: ICMP request for known host {}",
148 hostIpAddress);
149 byte[] destinationMacAddress = host.getMacAddress().toBytes();
150 srManager.addRouteToHost(sw,
Sangho Shin463bee52014-09-29 15:14:43 -0700151 destinationAddress.getInt(), destinationMacAddress);
Srikanth Vavilapalli5ddf8f02014-09-24 11:02:28 -0700152 return;
153 }
154 }
155 /* ICMP for an unknown host */
156 log.debug("ICMPHandler: ICMP request for unknown host {}"
157 + " and sending ARP request", destinationAddress);
Sangho Shin463bee52014-09-29 15:14:43 -0700158 srManager.sendArpRequest(sw, destinationAddress.getInt(), inPort);
Sangho Shin2f263692014-09-15 14:09:41 -0700159 }
160
161 }
Sangho Shineb083032014-09-22 16:11:34 -0700162 }
Sangho Shin2f263692014-09-15 14:09:41 -0700163
Sangho Shin463bee52014-09-29 15:14:43 -0700164
165
Sangho Shineb083032014-09-22 16:11:34 -0700166 /**
167 * Retrieve Gateway IP address of all subnets defined in net config file
168 *
169 * @param sw Switch to retrieve subnet GW IPs for
170 * @return list of GW IP addresses for all subnets
171 */
172 private List<String> getSubnetGatewayIps(Switch sw) {
173
174 List<String> gatewayIps = new ArrayList<String>();
175
176 String subnets = sw.getStringAttribute("subnets");
177 try {
178 JSONArray arry = new JSONArray(subnets);
179 for (int i = 0; i < arry.length(); i++) {
180 String subnetIpSlash = (String) arry.getJSONObject(i).get("subnetIp");
181 if (subnetIpSlash != null) {
182 String subnetIp = subnetIpSlash.substring(0, subnetIpSlash.indexOf('/'));
183 gatewayIps.add(subnetIp);
184 }
185 }
186 } catch (JSONException e) {
187 // TODO Auto-generated catch block
188 e.printStackTrace();
189 }
190
191 return gatewayIps;
Sangho Shin2f263692014-09-15 14:09:41 -0700192 }
193
Sangho Shin79c8d452014-09-18 09:50:21 -0700194
Sangho Shin79c8d452014-09-18 09:50:21 -0700195 /**
196 * Send ICMP reply back
197 *
198 * @param sw Switch
199 * @param inPort Port the ICMP packet is forwarded from
200 * @param icmpRequest the ICMP request to handle
201 * @param destinationAddress destination address to send ICMP response to
202 */
203 private void sendICMPResponse(Switch sw, Port inPort, Ethernet icmpRequest) {
204
205 Ethernet icmpReplyEth = new Ethernet();
206
207 IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
208 IPv4 icmpReplyIpv4 = new IPv4();
209 int destAddress = icmpRequestIpv4.getDestinationAddress();
210 icmpReplyIpv4.setDestinationAddress(icmpRequestIpv4.getSourceAddress());
211 icmpReplyIpv4.setSourceAddress(destAddress);
212 icmpReplyIpv4.setTtl((byte)64);
213 icmpReplyIpv4.setChecksum((short)0);
214
215
216 ICMP icmpReply = (ICMP)icmpRequestIpv4.getPayload().clone();
217 icmpReply.setIcmpCode((byte)0x00);
Sangho Shin1aa93542014-09-22 09:49:44 -0700218 icmpReply.setIcmpType((byte) ICMP_TYPE_REPLY);
Sangho Shin79c8d452014-09-18 09:50:21 -0700219 icmpReply.setChecksum((short)0);
220
221 icmpReplyIpv4.setPayload(icmpReply);
222
223 icmpReplyEth.setPayload(icmpReplyIpv4);
224 icmpReplyEth.setEtherType(Ethernet.TYPE_IPV4);
225 icmpReplyEth.setDestinationMACAddress(icmpRequest.getSourceMACAddress());
226 icmpReplyEth.setSourceMACAddress(icmpRequest.getDestinationMACAddress());
227
Sangho Shin1aa93542014-09-22 09:49:44 -0700228 sendPacketOut(sw, icmpReplyEth, new SwitchPort(sw.getDpid(), inPort.getPortNumber()), false);
Sangho Shin79c8d452014-09-18 09:50:21 -0700229
230 log.debug("Send an ICMP response {}", icmpReplyIpv4.toString());
231
232 }
233
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700234 /**
Sangho Shin1aa93542014-09-22 09:49:44 -0700235 * Send PACKET_OUT message with actions
236 * If switches support OFPP_TABLE action, it sends out packet to TABLE port
237 * Otherwise, it sends the packet to the port the packet came from
238 * (in this case, MPLS label is added if the packet needs go through transit switches)
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700239 *
240 * @param sw Switch the packet came from
241 * @param packet Ethernet packet to send
242 * @param switchPort port to send the packet
243 */
Sangho Shin1aa93542014-09-22 09:49:44 -0700244 private void sendPacketOut(Switch sw, Ethernet packet, SwitchPort switchPort, boolean supportOfppTable) {
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700245
246 boolean sameSubnet = false;
247 IOFSwitch ofSwitch = floodlightProvider.getMasterSwitch(sw.getDpid().value());
248 OFFactory factory = ofSwitch.getFactory();
249
250 List<OFAction> actions = new ArrayList<>();
251
Sangho Shin1aa93542014-09-22 09:49:44 -0700252 // If OFPP_TABLE action is not supported in the switch, MPLS label needs to be set
253 // if the packet needs to be delivered crossing switches
254 if (!supportOfppTable) {
255 // Check if the destination is the host attached to the switch
256 int destinationAddress = ((IPv4)packet.getPayload()).getDestinationAddress();
257 for (net.onrc.onos.core.topology.Host host: mutableTopology.getHosts(switchPort)) {
258 IPv4Address hostIpAddress = IPv4Address.of(host.getIpAddress());
259 if (hostIpAddress != null && hostIpAddress.getInt() == destinationAddress) {
260 sameSubnet = true;
261 break;
262 }
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700263 }
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700264
Sangho Shin9c0f4c32014-09-26 16:02:38 -0700265 IPv4Address targetAddress = IPv4Address.of(((IPv4)packet.getPayload()).getDestinationAddress());
266 String destMacAddress = packet.getDestinationMAC().toString();
267 // If the destination host is not attached in the switch
268 // and the destination is not the neighbor switch, then add MPLS label
269 String targetMac = getRouterMACFromConfig(targetAddress);
270 if (!sameSubnet && !targetMac.equals(destMacAddress)) {
Sangho Shin1aa93542014-09-22 09:49:44 -0700271 int mplsLabel = getMplsLabelFromConfig(targetAddress);
272 if (mplsLabel > 0) {
273 OFAction pushlabel = factory.actions().pushMpls(EthType.MPLS_UNICAST);
274 OFOxmMplsLabel l = factory.oxms()
275 .mplsLabel(U32.of(mplsLabel));
276 OFAction setlabelid = factory.actions().buildSetField()
277 .setField(l).build();
278 OFAction copyTtlOut = factory.actions().copyTtlOut();
279 actions.add(pushlabel);
280 actions.add(setlabelid);
281 actions.add(copyTtlOut);
282 }
283 }
284
285 OFAction outport = factory.actions().output(OFPort.of(switchPort.getPortNumber().shortValue()), Short.MAX_VALUE);
286 actions.add(outport);
287 }
288 // If OFPP_TABLE action is supported, first set a rule to allow packet from CONTROLLER port.
289 // Then, send the packet to the table port
290 else {
291 if (!controllerPortAllowed) {
292 addControlPortInVlanTable(sw);
293 controllerPortAllowed = true;
294 }
295 OFAction outport = factory.actions().output(OFPort.TABLE, Short.MAX_VALUE);
296 actions.add(outport);
297 }
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700298
299 OFPacketOut po = factory.buildPacketOut()
300 .setData(packet.serialize())
301 .setActions(actions)
302 .build();
303
304 flowPusher.add(sw.getDpid(), po);
305 }
306
Sangho Shin2f263692014-09-15 14:09:41 -0700307 /**
Sangho Shin1aa93542014-09-22 09:49:44 -0700308 * Get MPLS label for the target address from the network config file
309 *
310 * @param targetAddress - IP address of the target host
311 * @return MPLS label of the switch to send packets to the target address
312 */
313 private int getMplsLabelFromConfig(IPv4Address targetAddress) {
314
315 int mplsLabel = -1;
316
317 for (Switch sw: mutableTopology.getSwitches()) {
318
319 String subnets = sw.getStringAttribute("subnets");
320 try {
321 JSONArray arry = new JSONArray(subnets);
322 for (int i = 0; i < arry.length(); i++) {
323 String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
324 if (srManager.netMatch(subnetIp, targetAddress.toString())) {
325 String mplsLabelStr = sw.getStringAttribute("nodeSid");
326 if (mplsLabelStr != null)
327 mplsLabel = Integer.parseInt(mplsLabelStr);
328 }
329 }
330 } catch (JSONException e) {
331 // TODO Auto-generated catch block
332 e.printStackTrace();
333 }
334 }
335
336 return mplsLabel;
337 }
338
Sangho Shin2f263692014-09-15 14:09:41 -0700339
Sangho Shin9c0f4c32014-09-26 16:02:38 -0700340 /**
341 * Get Router MAC Address for the target address from the network config file
342 *
343 * @param targetAddress - IP address of the target host
344 * @return Router MAC of the switch to send packets to the target address
345 */
346 private String getRouterMACFromConfig(IPv4Address targetAddress) {
347
348 String routerMac = null;
349
350 for (Switch sw: mutableTopology.getSwitches()) {
351
352 String subnets = sw.getStringAttribute("subnets");
353 try {
354 JSONArray arry = new JSONArray(subnets);
355 for (int i = 0; i < arry.length(); i++) {
356 String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
357 if (srManager.netMatch(subnetIp, targetAddress.toString())) {
358 routerMac = sw.getStringAttribute("routerMac");
359 }
360 }
361 } catch (JSONException e) {
362 // TODO Auto-generated catch block
363 e.printStackTrace();
364 }
365 }
366
367 return routerMac;
368 }
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700369
370 /**
371 * Add a new rule to VLAN table to forward packets from any port to the next table
372 * It is required to forward packets from controller to pipeline
373 *
374 * @param sw Switch the packet came from
375 */
376 private void addControlPortInVlanTable(Switch sw) {
377
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700378 IOFSwitch ofSwitch = floodlightProvider.getMasterSwitch(sw.getDpid().value());
379 OFFactory factory = ofSwitch.getFactory();
380
Sangho Shin1aa93542014-09-22 09:49:44 -0700381 OFOxmInPort oxp = factory.oxms().inPort(OFPort.CONTROLLER);
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700382 OFOxmVlanVid oxv = factory.oxms()
383 .vlanVid(OFVlanVidMatch.UNTAGGED);
384 OFOxmList oxmList = OFOxmList.of(oxv);
385
Sangho Shin1aa93542014-09-22 09:49:44 -0700386 /* Cqpd switch does not seems to support CONTROLLER port as in_port match rule */
387 //OFOxmList oxmList = OFOxmList.of(oxp, oxv);
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700388
389 OFMatchV3 match = factory.buildMatchV3()
390 .setOxmList(oxmList)
391 .build();
392
393 OFInstruction gotoTbl = factory.instructions().buildGotoTable()
394 .setTableId(TableId.of(TABLE_TMAC)).build();
395 List<OFInstruction> instructions = new ArrayList<OFInstruction>();
Sangho Shinf41ac0a2014-09-19 09:50:30 -0700396 instructions.add(gotoTbl);
397 OFMessage flowEntry = factory.buildFlowAdd()
398 .setTableId(TableId.of(TABLE_VLAN))
399 .setMatch(match)
400 .setInstructions(instructions)
401 .setPriority(1000) // does not matter - all rules
402 // exclusive
403 .setBufferId(OFBufferId.NO_BUFFER)
404 .setIdleTimeout(0)
405 .setHardTimeout(0)
406 //.setXid(getNextTransactionId())
407 .build();
408
409 flowPusher.add(sw.getDpid(), flowEntry);;
Sangho Shin1aa93542014-09-22 09:49:44 -0700410 log.debug("Adding a new vlan-rules in sw {}", sw.getDpid());
Sangho Shin2f263692014-09-15 14:09:41 -0700411
412 }
413
414}