Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 1 | /* |
Brian O'Connor | a09fe5b | 2017-08-03 21:12:30 -0700 | [diff] [blame] | 2 | * Copyright 2017-present Open Networking Foundation |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 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 | */ |
| 16 | package org.onosproject.ofagent.impl; |
| 17 | |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableSet; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 19 | import com.google.common.collect.Lists; |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 20 | import com.google.common.collect.Sets; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 21 | import io.netty.channel.Channel; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 22 | import org.onlab.osgi.ServiceDirectory; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 23 | import org.onosproject.core.ApplicationId; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 24 | import org.onosproject.incubator.net.virtual.NetworkId; |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 25 | import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 26 | import org.onosproject.incubator.net.virtual.VirtualNetworkService; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 27 | import org.onosproject.net.ConnectPoint; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 28 | import org.onosproject.net.DeviceId; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 29 | import org.onosproject.net.Port; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 30 | import org.onosproject.net.PortNumber; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 31 | import org.onosproject.net.device.PortStatistics; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 32 | import org.onosproject.net.driver.DriverService; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 33 | import org.onosproject.net.flow.FlowEntry; |
| 34 | import org.onosproject.net.flow.FlowRule; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 35 | import org.onosproject.net.flow.FlowRuleService; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 36 | import org.onosproject.net.flow.TableStatisticsEntry; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 37 | import org.onosproject.net.group.DefaultGroupDescription; |
| 38 | import org.onosproject.net.group.DefaultGroupKey; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 39 | import org.onosproject.net.group.Group; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 40 | import org.onosproject.net.group.GroupBuckets; |
| 41 | import org.onosproject.net.group.GroupDescription; |
| 42 | import org.onosproject.net.group.GroupKey; |
| 43 | import org.onosproject.net.group.GroupService; |
| 44 | import org.onosproject.net.meter.Band; |
| 45 | import org.onosproject.net.meter.DefaultBand; |
| 46 | import org.onosproject.net.meter.DefaultMeterRequest; |
| 47 | import org.onosproject.net.meter.Meter; |
| 48 | import org.onosproject.net.meter.MeterId; |
| 49 | import org.onosproject.net.meter.MeterRequest; |
| 50 | import org.onosproject.net.meter.MeterService; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 51 | import org.onosproject.net.packet.InboundPacket; |
Jovana Vuleta | c884b69 | 2017-11-28 16:52:35 +0100 | [diff] [blame] | 52 | import org.onosproject.ofagent.api.OFAgent; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 53 | import org.onosproject.ofagent.api.OFSwitch; |
| 54 | import org.onosproject.ofagent.api.OFSwitchCapabilities; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 55 | import org.onosproject.ofagent.api.OFSwitchService; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 56 | import org.onosproject.openflow.controller.Dpid; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 57 | import org.projectfloodlight.openflow.protocol.OFActionType; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 58 | import org.projectfloodlight.openflow.protocol.OFBadRequestCode; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 59 | import org.projectfloodlight.openflow.protocol.OFBarrierReply; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 60 | import org.projectfloodlight.openflow.protocol.OFBucket; |
| 61 | import org.projectfloodlight.openflow.protocol.OFBucketCounter; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 62 | import org.projectfloodlight.openflow.protocol.OFControllerRole; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 63 | import org.projectfloodlight.openflow.protocol.OFEchoReply; |
| 64 | import org.projectfloodlight.openflow.protocol.OFEchoRequest; |
| 65 | import org.projectfloodlight.openflow.protocol.OFFactories; |
| 66 | import org.projectfloodlight.openflow.protocol.OFFactory; |
| 67 | import org.projectfloodlight.openflow.protocol.OFFeaturesReply; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 68 | import org.projectfloodlight.openflow.protocol.OFFlowMod; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 69 | import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 70 | import org.projectfloodlight.openflow.protocol.OFGetConfigReply; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 71 | import org.projectfloodlight.openflow.protocol.OFGroupAdd; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 72 | import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 73 | import org.projectfloodlight.openflow.protocol.OFGroupMod; |
| 74 | import org.projectfloodlight.openflow.protocol.OFGroupModify; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 75 | import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; |
| 76 | import org.projectfloodlight.openflow.protocol.OFGroupType; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 77 | import org.projectfloodlight.openflow.protocol.OFHello; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 78 | import org.projectfloodlight.openflow.protocol.OFMessage; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 79 | import org.projectfloodlight.openflow.protocol.OFMeterFeatures; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 80 | import org.projectfloodlight.openflow.protocol.OFMeterMod; |
| 81 | import org.projectfloodlight.openflow.protocol.OFMeterModFailedCode; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 82 | import org.projectfloodlight.openflow.protocol.OFPacketIn; |
| 83 | import org.projectfloodlight.openflow.protocol.OFPacketInReason; |
| 84 | import org.projectfloodlight.openflow.protocol.OFPacketOut; |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 85 | import org.projectfloodlight.openflow.protocol.OFPortConfig; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 86 | import org.projectfloodlight.openflow.protocol.OFPortDesc; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 87 | import org.projectfloodlight.openflow.protocol.OFPortMod; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 88 | import org.projectfloodlight.openflow.protocol.OFPortReason; |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 89 | import org.projectfloodlight.openflow.protocol.OFPortState; |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 90 | import org.projectfloodlight.openflow.protocol.OFPortStatsEntry; |
| 91 | import org.projectfloodlight.openflow.protocol.OFPortStatsRequest; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 92 | import org.projectfloodlight.openflow.protocol.OFPortStatus; |
Claudine Chiu | 7c6d51c | 2017-06-15 23:13:51 -0400 | [diff] [blame] | 93 | import org.projectfloodlight.openflow.protocol.OFRoleReply; |
| 94 | import org.projectfloodlight.openflow.protocol.OFRoleRequest; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 95 | import org.projectfloodlight.openflow.protocol.OFSetConfig; |
| 96 | import org.projectfloodlight.openflow.protocol.OFStatsReply; |
| 97 | import org.projectfloodlight.openflow.protocol.OFStatsRequest; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 98 | import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 99 | import org.projectfloodlight.openflow.protocol.OFType; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 100 | import org.projectfloodlight.openflow.protocol.OFVersion; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 101 | import org.projectfloodlight.openflow.protocol.action.OFAction; |
| 102 | import org.projectfloodlight.openflow.protocol.action.OFActionOutput; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 103 | import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 104 | import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 105 | import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 106 | import org.projectfloodlight.openflow.protocol.match.Match; |
| 107 | import org.projectfloodlight.openflow.protocol.match.MatchField; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 108 | import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand; |
| 109 | import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop; |
| 110 | import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark; |
| 111 | import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandExperimenter; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 112 | import org.projectfloodlight.openflow.types.DatapathId; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 113 | import org.projectfloodlight.openflow.types.OFGroup; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 114 | import org.projectfloodlight.openflow.types.OFPort; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 115 | import org.projectfloodlight.openflow.types.TableId; |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 116 | import org.projectfloodlight.openflow.types.U64; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 117 | import org.slf4j.Logger; |
| 118 | import org.slf4j.LoggerFactory; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 119 | |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 120 | import java.util.ArrayList; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 121 | import java.util.Collection; |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 122 | import java.util.Collections; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 123 | import java.util.List; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 124 | import java.util.Set; |
| 125 | import java.util.concurrent.ConcurrentHashMap; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 126 | import java.util.stream.Collectors; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 127 | |
| 128 | import static com.google.common.base.Preconditions.checkArgument; |
| 129 | import static com.google.common.base.Preconditions.checkNotNull; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 130 | import static org.projectfloodlight.openflow.protocol.OFControllerRole.ROLE_EQUAL; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 131 | |
| 132 | /** |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 133 | * Implementation of the default OpenFlow switch. |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 134 | */ |
| 135 | public final class DefaultOFSwitch implements OFSwitch { |
| 136 | |
| 137 | private static final String ERR_CH_DUPLICATE = "Channel already exists: "; |
| 138 | private static final String ERR_CH_NOT_FOUND = "Channel not found: "; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 139 | private static final long NUM_BUFFERS = 1024; |
| 140 | private static final short NUM_TABLES = 3; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 141 | |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 142 | private final Logger log; |
| 143 | |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 144 | private final OFSwitchService ofSwitchService; |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 145 | private final VirtualNetworkAdminService virtualNetworkAdminService; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 146 | private final FlowRuleService flowRuleService; |
| 147 | private final DriverService driverService; |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 148 | private final GroupService groupService; |
| 149 | private final MeterService meterService; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 150 | |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 151 | private final DatapathId dpId; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 152 | private final OFSwitchCapabilities capabilities; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 153 | private final NetworkId networkId; |
| 154 | private final DeviceId deviceId; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 155 | |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 156 | // miss_send_len field (in OFSetConfig and OFGetConfig messages) indicates the max |
| 157 | // bytes of a packet that the switch sends to the controller |
| 158 | private int missSendLen = 0xffff; |
| 159 | |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 160 | private final ConcurrentHashMap<Channel, OFControllerRole> controllerRoleMap |
| 161 | = new ConcurrentHashMap<>(); |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 162 | private static final OFFactory FACTORY = OFFactories.getFactory(OFVersion.OF_13); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 163 | |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 164 | private int handshakeTransactionIds = -1; |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 165 | |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 166 | private DefaultOFSwitch(DatapathId dpid, OFSwitchCapabilities capabilities, |
| 167 | NetworkId networkId, DeviceId deviceId, |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 168 | ServiceDirectory serviceDirectory) { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 169 | this.dpId = dpid; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 170 | this.capabilities = capabilities; |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 171 | this.networkId = networkId; |
| 172 | this.deviceId = deviceId; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 173 | this.ofSwitchService = serviceDirectory.get(OFSwitchService.class); |
| 174 | this.driverService = serviceDirectory.get(DriverService.class); |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 175 | this.virtualNetworkAdminService = serviceDirectory.get(VirtualNetworkAdminService.class); |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 176 | VirtualNetworkService virtualNetworkService = serviceDirectory.get(VirtualNetworkService.class); |
| 177 | this.flowRuleService = virtualNetworkService.get(networkId, FlowRuleService.class); |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 178 | this.groupService = virtualNetworkService.get(networkId, GroupService.class); |
| 179 | this.meterService = virtualNetworkService.get(networkId, MeterService.class); |
Jovana Vuleta | c884b69 | 2017-11-28 16:52:35 +0100 | [diff] [blame] | 180 | |
| 181 | log = LoggerFactory.getLogger(OFAgent.TRACER_LOG_TENANT_ID_PREFIX + virtualNetworkService.getTenantId(networkId) |
| 182 | + " " + getClass().getSimpleName() + " : " + dpid); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 183 | } |
| 184 | |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 185 | public static DefaultOFSwitch of(DatapathId dpid, OFSwitchCapabilities capabilities, |
| 186 | NetworkId networkId, DeviceId deviceId, |
| 187 | ServiceDirectory serviceDirectory) { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 188 | checkNotNull(dpid, "DPID cannot be null"); |
| 189 | checkNotNull(capabilities, "OF capabilities cannot be null"); |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 190 | return new DefaultOFSwitch(dpid, capabilities, networkId, deviceId, serviceDirectory); |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 191 | } |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 192 | |
| 193 | @Override |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 194 | public DatapathId dpid() { |
| 195 | return this.dpId; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | @Override |
| 199 | public OFSwitchCapabilities capabilities() { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 200 | return this.capabilities; |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public void addControllerChannel(Channel channel) { |
| 205 | controllerRoleMap.compute(channel, (ch, existing) -> { |
| 206 | final String error = ERR_CH_DUPLICATE + channel.remoteAddress(); |
| 207 | checkArgument(existing == null, error); |
| 208 | return ROLE_EQUAL; |
| 209 | }); |
| 210 | } |
| 211 | |
| 212 | @Override |
| 213 | public void deleteControllerChannel(Channel channel) { |
| 214 | if (controllerRoleMap.remove(channel) == null) { |
| 215 | final String error = ERR_CH_NOT_FOUND + channel.remoteAddress(); |
| 216 | throw new IllegalStateException(error); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | @Override |
| 221 | public void setRole(Channel channel, OFControllerRole role) { |
| 222 | controllerRoleMap.compute(channel, (ch, existing) -> { |
| 223 | final String error = ERR_CH_NOT_FOUND + channel.remoteAddress(); |
| 224 | checkNotNull(existing, error); |
| 225 | return role; |
| 226 | }); |
| 227 | } |
| 228 | |
| 229 | @Override |
| 230 | public OFControllerRole role(Channel channel) { |
| 231 | OFControllerRole role = controllerRoleMap.get(channel); |
| 232 | if (role == null) { |
| 233 | final String error = ERR_CH_NOT_FOUND + channel.remoteAddress(); |
| 234 | throw new IllegalStateException(error); |
| 235 | } |
| 236 | return role; |
| 237 | } |
| 238 | |
| 239 | @Override |
| 240 | public Set<Channel> controllerChannels() { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 241 | return ImmutableSet.copyOf(controllerRoleMap.keySet()); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | @Override |
| 245 | public void processPortAdded(Port port) { |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 246 | sendPortStatus(port, OFPortReason.ADD); |
| 247 | } |
| 248 | |
| 249 | @Override |
| 250 | public void processPortRemoved(Port port) { |
| 251 | sendPortStatus(port, OFPortReason.DELETE); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 252 | } |
| 253 | |
| 254 | @Override |
| 255 | public void processPortDown(Port port) { |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 256 | sendPortStatus(port, OFPortReason.MODIFY); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 257 | } |
| 258 | |
| 259 | @Override |
| 260 | public void processPortUp(Port port) { |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 261 | sendPortStatus(port, OFPortReason.MODIFY); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 262 | } |
| 263 | |
| 264 | @Override |
| 265 | public void processFlowRemoved(FlowRule flowRule) { |
| 266 | // TODO generate FLOW_REMOVED message and send it to the controller |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 267 | log.debug("processFlowRemoved: Functionality not yet supported for {}", flowRule); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | @Override |
| 271 | public void processPacketIn(InboundPacket packet) { |
| 272 | // TODO generate PACKET_IN message and send it to the controller |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 273 | log.debug("processPacketIn: Functionality not yet supported for {}", packet); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 274 | } |
| 275 | |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 276 | private void processPortMod(OFPortMod portMod) { |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 277 | // process specified port |
| 278 | PortNumber portNumber = PortNumber.portNumber(portMod.getPortNo().getPortNumber()); |
| 279 | boolean disablePort = portMod.getConfig().contains(OFPortConfig.PORT_DOWN); |
| 280 | log.debug("processing PORT_MOD message - setting port {} state to {}", |
| 281 | portNumber, !disablePort); |
| 282 | virtualNetworkAdminService.updatePortState(networkId, deviceId, portNumber, !disablePort); |
| 283 | // TODO what side effects (e.g. cleaning flow mods) needs to be handled? |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | private void processFlowMod(OFFlowMod flowMod) { |
| 287 | // convert OFFlowMod to FLowRule object |
| 288 | OFAgentVirtualFlowEntryBuilder flowEntryBuilder = |
| 289 | new OFAgentVirtualFlowEntryBuilder(deviceId, flowMod, driverService); |
| 290 | FlowEntry flowEntry = flowEntryBuilder.build(); |
| 291 | flowRuleService.applyFlowRules(flowEntry); |
| 292 | } |
| 293 | |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 294 | // Methods that support GROUP_MOD |
| 295 | |
| 296 | private GroupDescription.Type getGroupType(OFGroupType type) { |
| 297 | switch (type) { |
| 298 | case ALL: |
| 299 | return GroupDescription.Type.ALL; |
| 300 | case INDIRECT: |
| 301 | return GroupDescription.Type.INDIRECT; |
| 302 | case SELECT: |
| 303 | return GroupDescription.Type.SELECT; |
| 304 | case FF: |
| 305 | return GroupDescription.Type.FAILOVER; |
| 306 | default: |
| 307 | log.error("Unsupported OF group type : {}", type); |
| 308 | break; |
| 309 | } |
| 310 | return null; |
| 311 | } |
| 312 | |
| 313 | private void processGroupMod(OFGroupMod groupMod) { |
| 314 | log.debug("processing GROUP_MOD {} message", groupMod.getCommand()); |
| 315 | |
| 316 | ApplicationId appId = ofSwitchService.appId(); |
| 317 | GroupKey appCookie = new DefaultGroupKey(networkId.toString().getBytes()); |
| 318 | switch (groupMod.getCommand()) { |
| 319 | case ADD: |
| 320 | // TODO return OFGroupModFailedCode.GROUP_EXISTS if group already exists |
| 321 | int groupId = groupMod.getGroup().getGroupNumber(); |
| 322 | OFGroupAdd groupAdd = (OFGroupAdd) groupMod; |
| 323 | GroupBuckets groupAddBuckets = new OFAgentVirtualGroupBucketEntryBuilder( |
| 324 | Dpid.dpid(Dpid.uri(dpid().getLong())), |
| 325 | groupAdd.getBuckets(), groupAdd.getGroupType(), driverService) |
| 326 | .build(); |
| 327 | GroupDescription groupDescription = new DefaultGroupDescription( |
| 328 | deviceId, getGroupType(groupAdd.getGroupType()), groupAddBuckets, |
| 329 | appCookie, groupId, appId); |
| 330 | groupService.addGroup(groupDescription); |
| 331 | break; |
| 332 | case MODIFY: |
| 333 | // TODO return OFGroupModFailedCode.INVALID_GROUP if group does not exist |
| 334 | OFGroupModify groupModify = (OFGroupModify) groupMod; |
| 335 | GroupBuckets groupModifyBuckets = new OFAgentVirtualGroupBucketEntryBuilder( |
| 336 | Dpid.dpid(Dpid.uri(dpid().getLong())), |
| 337 | groupModify.getBuckets(), groupModify.getGroupType(), driverService) |
| 338 | .build(); |
| 339 | groupService.setBucketsForGroup(deviceId, appCookie, groupModifyBuckets, |
| 340 | appCookie, appId); |
| 341 | break; |
| 342 | case DELETE: |
| 343 | groupService.removeGroup(deviceId, appCookie, appId); |
| 344 | break; |
| 345 | default: |
| 346 | // INSERT_BUCKET, REMOVE_BUCKET are effective OF 1.5. OFAgent supports 1.3. |
| 347 | log.warn("Unsupported GROUP_MOD {} message received for switch {}", |
| 348 | groupMod.getCommand(), this); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | // Methods that spport METER_MOD. |
| 353 | |
| 354 | private Band band(OFMeterBand ofMeterBand) { |
| 355 | DefaultBand.Builder builder = DefaultBand.builder(); |
| 356 | if (ofMeterBand instanceof OFMeterBandDrop) { |
| 357 | OFMeterBandDrop ofMeterBandDrop = (OFMeterBandDrop) ofMeterBand; |
| 358 | builder.ofType(Band.Type.DROP) |
| 359 | .burstSize(ofMeterBandDrop.getBurstSize()) |
| 360 | .withRate(ofMeterBandDrop.getRate()); |
| 361 | } else if (ofMeterBand instanceof OFMeterBandDscpRemark) { |
| 362 | OFMeterBandDscpRemark ofMeterBandDscpRemark = (OFMeterBandDscpRemark) ofMeterBand; |
| 363 | builder.ofType(Band.Type.REMARK) |
| 364 | .burstSize(ofMeterBandDscpRemark.getBurstSize()) |
| 365 | .withRate(ofMeterBandDscpRemark.getRate()) |
| 366 | .dropPrecedence(ofMeterBandDscpRemark.getPrecLevel()); |
| 367 | } else if (ofMeterBand instanceof OFMeterBandExperimenter) { |
| 368 | OFMeterBandExperimenter ofMeterBandExperimenter = (OFMeterBandExperimenter) ofMeterBand; |
| 369 | builder.ofType(Band.Type.EXPERIMENTAL) |
| 370 | .burstSize(ofMeterBandExperimenter.getBurstSize()) |
| 371 | .withRate(ofMeterBandExperimenter.getRate()); |
| 372 | } |
| 373 | return builder.build(); |
| 374 | } |
| 375 | |
| 376 | private MeterRequest.Builder meterRequestBuilder(OFMeterMod meterMod) { |
| 377 | Collection<Band> bands = meterMod.getBands().stream() |
| 378 | .map(ofMeterBand -> band(ofMeterBand)).collect(Collectors.toList()); |
| 379 | return DefaultMeterRequest.builder().forDevice(deviceId) |
| 380 | .withBands(bands).fromApp(ofSwitchService.appId()); |
| 381 | } |
| 382 | |
| 383 | private void meterModError(OFMeterMod meterMod, OFMeterModFailedCode code, |
| 384 | Channel channel) { |
| 385 | OFMeterModFailedErrorMsg errorMsg = FACTORY.errorMsgs() |
| 386 | .buildMeterModFailedErrorMsg() |
| 387 | .setXid(meterMod.getXid()) |
| 388 | .setCode(code) |
| 389 | .build(); |
| 390 | channel.writeAndFlush(Collections.singletonList(errorMsg)); |
| 391 | log.debug("Sent meterMod error {}", code); |
| 392 | } |
| 393 | |
| 394 | private void processMeterMod(OFMeterMod meterMod, Channel channel) { |
| 395 | log.debug("processing METER_MOD {} message", meterMod.getCommand()); |
| 396 | |
| 397 | long meterModId = meterMod.getMeterId(); |
| 398 | Meter existingMeter = meterService.getMeter(deviceId, MeterId.meterId(meterModId)); |
| 399 | MeterRequest meterRequest = null; |
| 400 | switch (meterMod.getCommand()) { |
| 401 | case ADD: |
| 402 | if (existingMeter != null) { |
| 403 | meterModError(meterMod, OFMeterModFailedCode.METER_EXISTS, channel); |
| 404 | return; |
| 405 | } |
| 406 | meterRequest = meterRequestBuilder(meterMod).add(); |
| 407 | break; |
| 408 | case MODIFY: |
| 409 | if (existingMeter == null) { |
| 410 | meterModError(meterMod, OFMeterModFailedCode.UNKNOWN_METER, channel); |
| 411 | return; |
| 412 | } |
| 413 | meterRequest = meterRequestBuilder(meterMod).add(); |
| 414 | break; |
| 415 | case DELETE: |
| 416 | // non-existing meter id will not result in OFMeterModFailedErrorMsg |
| 417 | // being sent to the controller |
| 418 | meterRequest = meterRequestBuilder(meterMod).remove(); |
| 419 | break; |
| 420 | default: |
| 421 | log.warn("Unexpected message {} received for switch {}", |
| 422 | meterMod.getCommand(), this); |
| 423 | return; |
| 424 | } |
| 425 | meterService.submit(meterRequest); |
| 426 | } |
| 427 | |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 428 | @Override |
| 429 | public void processControllerCommand(Channel channel, OFMessage msg) { |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 430 | |
| 431 | OFControllerRole myRole = role(channel); |
| 432 | if (OFControllerRole.ROLE_SLAVE.equals(myRole)) { |
| 433 | OFBadRequestErrorMsg errorMsg = FACTORY.errorMsgs() |
| 434 | .buildBadRequestErrorMsg() |
| 435 | .setXid(msg.getXid()) |
| 436 | .setCode(OFBadRequestCode.IS_SLAVE) |
| 437 | .build(); |
| 438 | channel.writeAndFlush(Collections.singletonList(errorMsg)); |
| 439 | return; |
| 440 | } |
| 441 | |
| 442 | switch (msg.getType()) { |
| 443 | case PORT_MOD: |
| 444 | OFPortMod portMod = (OFPortMod) msg; |
| 445 | processPortMod(portMod); |
| 446 | break; |
| 447 | case FLOW_MOD: |
| 448 | OFFlowMod flowMod = (OFFlowMod) msg; |
| 449 | processFlowMod(flowMod); |
| 450 | break; |
| 451 | case GROUP_MOD: |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 452 | OFGroupMod groupMod = (OFGroupMod) msg; |
| 453 | processGroupMod(groupMod); |
| 454 | break; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 455 | case METER_MOD: |
Claudine Chiu | b211b87 | 2017-09-05 17:27:00 -0400 | [diff] [blame] | 456 | OFMeterMod meterMod = (OFMeterMod) msg; |
| 457 | processMeterMod(meterMod, channel); |
| 458 | break; |
Claudine Chiu | 20cbd45 | 2017-08-30 19:23:11 -0400 | [diff] [blame] | 459 | case TABLE_MOD: |
| 460 | log.debug("processControllerCommand: {} not yet supported for {}", |
| 461 | msg.getType(), msg); |
| 462 | break; |
| 463 | default: |
| 464 | log.warn("Unexpected message {} received for switch {}", |
| 465 | msg.getType(), this); |
| 466 | } |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 467 | } |
| 468 | |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 469 | private void sendPortStatus(Port port, OFPortReason ofPortReason) { |
| 470 | Set<Channel> channels = controllerChannels(); |
| 471 | if (channels.isEmpty()) { |
| 472 | log.trace("No channels present. Port status will not be sent."); |
| 473 | return; |
| 474 | } |
| 475 | OFPortDesc ofPortDesc = portDesc(port); |
| 476 | OFPortStatus ofPortStatus = FACTORY.buildPortStatus() |
| 477 | .setDesc(ofPortDesc) |
| 478 | .setReason(ofPortReason) |
| 479 | .build(); |
| 480 | log.trace("Sending port status {}", ofPortStatus); |
| 481 | channels.forEach(channel -> { |
| 482 | channel.writeAndFlush(Collections.singletonList(ofPortStatus)); |
| 483 | }); |
| 484 | } |
| 485 | |
| 486 | private OFPortDesc portDesc(Port port) { |
| 487 | OFPort ofPort = OFPort.of((int) port.number().toLong()); |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 488 | Set<OFPortConfig> portConfigs = Sets.newHashSet(); |
| 489 | Set<OFPortState> portStates = Sets.newHashSet(); |
| 490 | if (!port.isEnabled()) { |
| 491 | portConfigs.add(OFPortConfig.PORT_DOWN); |
| 492 | portStates.add(OFPortState.LINK_DOWN); |
| 493 | } |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 494 | OFPortDesc ofPortDesc = FACTORY.buildPortDesc() |
| 495 | .setPortNo(ofPort) |
Claudine Chiu | e92efea | 2017-10-31 13:58:44 -0400 | [diff] [blame] | 496 | .setState(portStates) |
| 497 | .setConfig(portConfigs) |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 498 | .build(); |
| 499 | return ofPortDesc; |
| 500 | } |
| 501 | |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 502 | private OFPortStatsEntry portStatsEntry(PortStatistics portStatistic) { |
| 503 | OFPortStatsEntry ofPortStatsEntry = FACTORY.buildPortStatsEntry() |
| 504 | .setPortNo(OFPort.of(portStatistic.port())) |
| 505 | .setTxBytes(U64.of(portStatistic.bytesSent())) |
| 506 | .setTxPackets(U64.of(portStatistic.packetsSent())) |
| 507 | .setTxDropped(U64.of(portStatistic.packetsTxDropped())) |
| 508 | .setTxErrors(U64.of(portStatistic.packetsTxErrors())) |
| 509 | .setRxBytes(U64.of(portStatistic.bytesReceived())) |
| 510 | .setRxPackets(U64.of(portStatistic.packetsReceived())) |
| 511 | .setRxDropped(U64.of(portStatistic.packetsRxDropped())) |
| 512 | .setRxErrors(U64.of(portStatistic.packetsRxErrors())) |
| 513 | .setDurationSec(portStatistic.durationSec()) |
| 514 | .setDurationNsec(portStatistic.durationNano()) |
| 515 | .build(); |
| 516 | return ofPortStatsEntry; |
| 517 | } |
| 518 | |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 519 | private OFFlowStatsEntry ofFlowStatsEntry(FlowEntry flowEntry) { |
| 520 | // TODO get match from flowEntry.selector() |
| 521 | Match.Builder matchB = FACTORY.buildMatch(); |
| 522 | OFActionOutput actionOutput = FACTORY.actions() |
| 523 | .buildOutput().build(); |
| 524 | // TODO get instructions from flowEntry.treatment() |
| 525 | OFInstruction instruction = FACTORY.instructions() |
| 526 | .applyActions(Collections.singletonList(actionOutput)); |
| 527 | OFFlowStatsEntry ofFlowStatsEntry = FACTORY.buildFlowStatsEntry() |
| 528 | .setMatch(matchB.build()) |
| 529 | .setInstructions(Collections.singletonList(instruction)) |
| 530 | .setTableId(TableId.of(flowEntry.tableId())) |
| 531 | .setHardTimeout(flowEntry.hardTimeout()) |
| 532 | .setIdleTimeout(flowEntry.timeout()) |
| 533 | .setCookie(U64.of(flowEntry.id().value())) |
| 534 | .setPriority(flowEntry.priority()) |
| 535 | .setDurationSec(flowEntry.life()) |
| 536 | .setPacketCount(U64.of(flowEntry.packets())) |
| 537 | .setByteCount(U64.of(flowEntry.bytes())) |
| 538 | .build(); |
| 539 | return ofFlowStatsEntry; |
| 540 | } |
| 541 | |
| 542 | private OFTableStatsEntry ofFlowTableStatsEntry(TableStatisticsEntry tableStatisticsEntry) { |
| 543 | OFTableStatsEntry ofTableStatsEntry = FACTORY.buildTableStatsEntry() |
| 544 | .setTableId(TableId.of(tableStatisticsEntry.tableId())) |
| 545 | .setActiveCount(tableStatisticsEntry.activeFlowEntries()) |
| 546 | .setLookupCount(U64.of(tableStatisticsEntry.packetsLookedup())) |
| 547 | .setMatchedCount(U64.of(tableStatisticsEntry.packetsLookedup())) |
| 548 | .build(); |
| 549 | return ofTableStatsEntry; |
| 550 | } |
| 551 | |
| 552 | private OFGroupStatsEntry ofGroupStatsEntry(Group group) { |
| 553 | List<OFBucketCounter> ofBucketCounters = Lists.newArrayList(); |
| 554 | group.buckets().buckets().forEach(groupBucket -> { |
| 555 | ofBucketCounters.add(FACTORY.bucketCounter( |
| 556 | U64.of(groupBucket.packets()), U64.of(groupBucket.bytes()))); |
| 557 | }); |
| 558 | OFGroupStatsEntry entry = FACTORY.buildGroupStatsEntry() |
| 559 | .setGroup(OFGroup.of(group.id().id())) |
| 560 | .setDurationSec(group.life()) |
| 561 | .setPacketCount(U64.of(group.packets())) |
| 562 | .setByteCount(U64.of(group.bytes())) |
| 563 | .setRefCount(group.referenceCount()) |
| 564 | .setBucketStats(ofBucketCounters) |
| 565 | .build(); |
| 566 | return entry; |
| 567 | } |
| 568 | |
| 569 | private OFGroupDescStatsEntry ofGroupDescStatsEntry(Group group) { |
| 570 | List<OFBucket> ofBuckets = Lists.newArrayList(); |
| 571 | group.buckets().buckets().forEach(groupBucket -> { |
| 572 | ofBuckets.add(FACTORY.buildBucket() |
| 573 | .setWeight(groupBucket.weight()) |
| 574 | .setWatchGroup(OFGroup.of(groupBucket.watchGroup().id())) |
| 575 | .setWatchPort(OFPort.of((int) groupBucket.watchPort().toLong())) |
| 576 | .build() |
| 577 | ); |
| 578 | }); |
| 579 | OFGroup ofGroup = OFGroup.of(group.givenGroupId()); |
| 580 | OFGroupType ofGroupType = OFGroupType.valueOf(group.type().name()); |
| 581 | OFGroupDescStatsEntry entry = FACTORY.buildGroupDescStatsEntry() |
| 582 | .setGroup(ofGroup) |
| 583 | .setGroupType(ofGroupType) |
| 584 | .setBuckets(ofBuckets) |
| 585 | .build(); |
| 586 | return entry; |
| 587 | } |
| 588 | |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 589 | @Override |
| 590 | public void processStatsRequest(Channel channel, OFMessage msg) { |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 591 | if (msg.getType() != OFType.STATS_REQUEST) { |
| 592 | log.warn("Ignoring message of type {}.", msg.getType()); |
| 593 | return; |
| 594 | } |
| 595 | |
| 596 | OFStatsRequest ofStatsRequest = (OFStatsRequest) msg; |
| 597 | OFStatsReply ofStatsReply = null; |
| 598 | switch (ofStatsRequest.getStatsType()) { |
| 599 | case PORT_DESC: |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 600 | List<OFPortDesc> portDescs = new ArrayList<>(); |
| 601 | Set<Port> ports = ofSwitchService.ports(networkId, deviceId); |
| 602 | ports.forEach(port -> { |
| 603 | OFPortDesc ofPortDesc = portDesc(port); |
| 604 | portDescs.add(ofPortDesc); |
| 605 | }); |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 606 | ofStatsReply = FACTORY.buildPortDescStatsReply() |
| 607 | .setXid(msg.getXid()) |
Claudine Chiu | 785ef2d | 2017-07-04 13:13:28 -0400 | [diff] [blame] | 608 | .setEntries(portDescs) |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 609 | //TODO add details |
| 610 | .build(); |
| 611 | break; |
Claudine Chiu | 2729ffd | 2017-07-31 21:38:27 -0400 | [diff] [blame] | 612 | case PORT: |
| 613 | OFPortStatsRequest portStatsRequest = (OFPortStatsRequest) msg; |
| 614 | OFPort ofPort = portStatsRequest.getPortNo(); |
| 615 | List<OFPortStatsEntry> portStatsEntries = new ArrayList<>(); |
| 616 | List<PortStatistics> portStatistics = |
| 617 | ofSwitchService.getPortStatistics(networkId, deviceId); |
| 618 | if (ofPort.equals(OFPort.ANY)) { |
| 619 | portStatistics.forEach(portStatistic -> { |
| 620 | OFPortStatsEntry ofPortStatsEntry = portStatsEntry(portStatistic); |
| 621 | portStatsEntries.add(ofPortStatsEntry); |
| 622 | }); |
| 623 | } |
| 624 | ofStatsReply = FACTORY.buildPortStatsReply() |
| 625 | .setEntries(portStatsEntries) |
| 626 | .setXid(msg.getXid()) |
| 627 | .build(); |
| 628 | break; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 629 | case METER_FEATURES: |
| 630 | OFMeterFeatures ofMeterFeatures = FACTORY.buildMeterFeatures() |
| 631 | .build(); |
| 632 | ofStatsReply = FACTORY.buildMeterFeaturesStatsReply() |
| 633 | .setXid(msg.getXid()) |
| 634 | .setFeatures(ofMeterFeatures) |
| 635 | //TODO add details |
| 636 | .build(); |
| 637 | break; |
Claudine Chiu | ce8ccc6 | 2017-08-16 10:06:20 -0400 | [diff] [blame] | 638 | case FLOW: |
| 639 | List<OFFlowStatsEntry> flowStatsEntries = new ArrayList<>(); |
| 640 | List<FlowEntry> flowStats = ofSwitchService.getFlowEntries(networkId, deviceId); |
| 641 | flowStats.forEach(flowEntry -> { |
| 642 | OFFlowStatsEntry ofFlowStatsEntry = ofFlowStatsEntry(flowEntry); |
| 643 | flowStatsEntries.add(ofFlowStatsEntry); |
| 644 | }); |
| 645 | ofStatsReply = FACTORY.buildFlowStatsReply() |
| 646 | .setEntries(flowStatsEntries) |
| 647 | .setXid(msg.getXid()) |
| 648 | .build(); |
| 649 | break; |
| 650 | case TABLE: |
| 651 | List<OFTableStatsEntry> ofTableStatsEntries = new ArrayList<>(); |
| 652 | List<TableStatisticsEntry> tableStats = ofSwitchService.getFlowTableStatistics(networkId, deviceId); |
| 653 | tableStats.forEach(tableStatisticsEntry -> { |
| 654 | OFTableStatsEntry ofFlowStatsEntry = ofFlowTableStatsEntry(tableStatisticsEntry); |
| 655 | ofTableStatsEntries.add(ofFlowStatsEntry); |
| 656 | }); |
| 657 | ofStatsReply = FACTORY.buildTableStatsReply() |
| 658 | .setEntries(ofTableStatsEntries) |
| 659 | .setXid(msg.getXid()) |
| 660 | .build(); |
| 661 | break; |
| 662 | case GROUP: |
| 663 | List<Group> groupStats = ofSwitchService.getGroups(networkId, deviceId); |
| 664 | List<OFGroupStatsEntry> ofGroupStatsEntries = new ArrayList<>(); |
| 665 | groupStats.forEach(group -> { |
| 666 | OFGroupStatsEntry entry = ofGroupStatsEntry(group); |
| 667 | ofGroupStatsEntries.add(entry); |
| 668 | }); |
| 669 | ofStatsReply = FACTORY.buildGroupStatsReply() |
| 670 | .setEntries(ofGroupStatsEntries) |
| 671 | .setXid(msg.getXid()) |
| 672 | .build(); |
| 673 | break; |
| 674 | case GROUP_DESC: |
| 675 | List<OFGroupDescStatsEntry> ofGroupDescStatsEntries = new ArrayList<>(); |
| 676 | List<Group> groupStats2 = ofSwitchService.getGroups(networkId, deviceId); |
| 677 | groupStats2.forEach(group -> { |
| 678 | OFGroupDescStatsEntry entry = ofGroupDescStatsEntry(group); |
| 679 | ofGroupDescStatsEntries.add(entry); |
| 680 | }); |
| 681 | ofStatsReply = FACTORY.buildGroupDescStatsReply() |
| 682 | .setEntries(ofGroupDescStatsEntries) |
| 683 | .setXid(msg.getXid()) |
| 684 | .build(); |
| 685 | break; |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 686 | case DESC: |
| 687 | ofStatsReply = FACTORY.buildDescStatsReply() |
| 688 | .setXid(msg.getXid()) |
| 689 | .build(); |
| 690 | break; |
| 691 | default: |
| 692 | log.debug("Functionality not yet supported for type {} statsType{} msg {}", |
| 693 | msg.getType(), ofStatsRequest.getStatsType(), msg); |
| 694 | break; |
| 695 | } |
| 696 | |
| 697 | if (ofStatsReply != null) { |
| 698 | log.trace("request {}; reply {}", msg, ofStatsReply); |
| 699 | channel.writeAndFlush(Collections.singletonList(ofStatsReply)); |
| 700 | } |
| 701 | |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 702 | } |
| 703 | |
| 704 | @Override |
| 705 | public void processRoleRequest(Channel channel, OFMessage msg) { |
Claudine Chiu | 7c6d51c | 2017-06-15 23:13:51 -0400 | [diff] [blame] | 706 | OFRoleRequest ofRoleRequest = (OFRoleRequest) msg; |
| 707 | OFControllerRole oldRole = role(channel); |
| 708 | OFControllerRole newRole = ofRoleRequest.getRole(); |
| 709 | if (oldRole.equals(newRole)) { |
| 710 | log.trace("No change needed to existing role {}", oldRole); |
| 711 | } else { |
| 712 | log.trace("Changing role from {} to {}", oldRole, newRole); |
| 713 | setRole(channel, newRole); |
| 714 | } |
| 715 | OFRoleReply ofRoleReply = FACTORY.buildRoleReply() |
| 716 | .setRole(role(channel)) |
| 717 | .setXid(msg.getXid()) |
| 718 | .build(); |
| 719 | channel.writeAndFlush(Collections.singletonList(ofRoleReply)); |
| 720 | log.trace("request {}; reply {}", msg, ofRoleReply); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 721 | } |
| 722 | |
| 723 | @Override |
| 724 | public void processFeaturesRequest(Channel channel, OFMessage msg) { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 725 | OFFeaturesReply ofFeaturesReply = FACTORY.buildFeaturesReply() |
| 726 | .setDatapathId(dpId) |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 727 | .setNBuffers(NUM_BUFFERS) |
| 728 | .setNTables(NUM_TABLES) |
| 729 | .setCapabilities(capabilities.ofSwitchCapabilities()) |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 730 | .setXid(msg.getXid()) |
| 731 | .build(); |
| 732 | channel.writeAndFlush(Collections.singletonList(ofFeaturesReply)); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 733 | } |
| 734 | |
| 735 | @Override |
| 736 | public void processLldp(Channel channel, OFMessage msg) { |
Claudine Chiu | 5c184e1 | 2017-08-08 21:21:38 -0400 | [diff] [blame] | 737 | log.trace("processLldp msg{}", msg); |
| 738 | |
| 739 | // For each output port, look up neighbour port. |
| 740 | // If neighbour port exists, have the neighbour switch send lldp response. |
| 741 | // Modeled after how OpenVirtex handles lldp from external controller. |
| 742 | OFPacketOut ofPacketOut = (OFPacketOut) msg; |
| 743 | List<OFAction> actions = ofPacketOut.getActions(); |
| 744 | for (final OFAction action : actions) { |
| 745 | OFActionType actionType = action.getType(); |
| 746 | if (actionType.equals(OFActionType.OUTPUT)) { |
| 747 | OFActionOutput ofActionOutput = (OFActionOutput) action; |
| 748 | OFPort ofPort = ofActionOutput.getPort(); |
| 749 | ConnectPoint neighbourCp = |
| 750 | ofSwitchService.neighbour(networkId, deviceId, |
| 751 | PortNumber.portNumber(ofPort.getPortNumber())); |
| 752 | if (neighbourCp == null) { |
| 753 | log.trace("No neighbour found for {} {}", deviceId, ofPort); |
| 754 | continue; |
| 755 | } |
| 756 | OFSwitch neighbourSwitch = ofSwitchService.ofSwitch(networkId, |
| 757 | neighbourCp.deviceId()); |
| 758 | neighbourSwitch.sendLldpResponse(ofPacketOut, neighbourCp.port()); |
| 759 | } |
| 760 | } |
| 761 | } |
| 762 | |
| 763 | @Override |
| 764 | public void sendLldpResponse(OFPacketOut po, PortNumber inPort) { |
| 765 | Match.Builder matchB = FACTORY.buildMatch(); |
| 766 | matchB.setExact(MatchField.IN_PORT, OFPort.of((int) inPort.toLong())); |
| 767 | OFPacketIn pi = FACTORY.buildPacketIn() |
| 768 | .setBufferId(po.getBufferId()) |
| 769 | .setMatch(matchB.build()) |
| 770 | .setReason(OFPacketInReason.NO_MATCH) |
| 771 | .setData(po.getData()) |
| 772 | .build(); |
| 773 | log.trace("Sending packet in {}", pi); |
| 774 | controllerChannels().forEach(channel -> { |
| 775 | channel.writeAndFlush(Collections.singletonList(pi)); |
| 776 | }); |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 777 | } |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 778 | |
| 779 | @Override |
| 780 | public void sendOfHello(Channel channel) { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 781 | OFHello ofHello = FACTORY.buildHello() |
| 782 | .setXid(this.handshakeTransactionIds--) |
| 783 | .build(); |
| 784 | channel.writeAndFlush(Collections.singletonList(ofHello)); |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 785 | } |
| 786 | |
| 787 | @Override |
| 788 | public void processEchoRequest(Channel channel, OFMessage msg) { |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 789 | OFEchoReply ofEchoReply = FACTORY.buildEchoReply() |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 790 | .setXid(msg.getXid()) |
Hyunsun Moon | f4ba44f | 2017-03-14 03:25:52 +0900 | [diff] [blame] | 791 | .setData(((OFEchoRequest) msg).getData()) |
| 792 | .build(); |
| 793 | channel.writeAndFlush(Collections.singletonList(ofEchoReply)); |
Daniel Park | be6b673 | 2016-11-11 15:52:19 +0900 | [diff] [blame] | 794 | } |
Claudine Chiu | e2d5acc | 2017-06-08 22:49:21 -0400 | [diff] [blame] | 795 | |
| 796 | @Override |
| 797 | public void processGetConfigRequest(Channel channel, OFMessage msg) { |
| 798 | OFGetConfigReply ofGetConfigReply = FACTORY.buildGetConfigReply() |
| 799 | .setXid(msg.getXid()) |
| 800 | .setMissSendLen(missSendLen) |
| 801 | .build(); |
| 802 | log.trace("request {}; reply {}", msg, ofGetConfigReply); |
| 803 | channel.writeAndFlush(Collections.singletonList(ofGetConfigReply)); |
| 804 | } |
| 805 | |
| 806 | @Override |
| 807 | public void processSetConfigMessage(Channel channel, OFMessage msg) { |
| 808 | OFSetConfig ofSetConfig = (OFSetConfig) msg; |
| 809 | if (missSendLen != ofSetConfig.getMissSendLen()) { |
| 810 | log.trace("Changing missSendLen from {} to {}.", |
| 811 | missSendLen, ofSetConfig.getMissSendLen()); |
| 812 | missSendLen = ofSetConfig.getMissSendLen(); |
| 813 | } |
| 814 | |
| 815 | // SetConfig message is not acknowledged |
| 816 | } |
| 817 | |
| 818 | @Override |
| 819 | public void processBarrierRequest(Channel channel, OFMessage msg) { |
| 820 | // TODO check previous state requests have been handled before issuing BarrierReply |
| 821 | OFBarrierReply ofBarrierReply = FACTORY.buildBarrierReply() |
| 822 | .setXid(msg.getXid()) |
| 823 | .build(); |
| 824 | log.trace("request {}; reply {}", msg, ofBarrierReply); |
| 825 | channel.writeAndFlush(Collections.singletonList(ofBarrierReply)); |
| 826 | } |
Hyunsun Moon | 90163ba | 2016-10-12 13:35:14 -0700 | [diff] [blame] | 827 | } |